From a7e1c1c08854930ddb4366698d5bf079ae0536f3 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 13 Apr 2017 16:24:38 -0700 Subject: [PATCH] Only add widgets system shortcut if widgets exist Before we were adding it in a disabled state, and then enabling it once widgets were bound (if widgets existed for that app). Now we load all widgets when launcher starts so that we can use the values for the purpose of animating the popup container. Then, as the container opens, we reload widgets/shortcuts for that particular app and add/remove the widgets shortcut if necessary. Bug: 34940468 Change-Id: I64bd009442d10d3d1f9a977bdedfdb639a7dd193 --- src/com/android/launcher3/LauncherModel.java | 10 +++ .../launcher3/model/PackageUpdatedTask.java | 9 +++ .../popup/PopupContainerWithArrow.java | 24 +++---- .../launcher3/popup/PopupDataProvider.java | 13 +++- .../launcher3/popup/PopupPopulator.java | 60 +++++++++--------- .../launcher3/popup/SystemShortcut.java | 1 - .../shortcuts/ShortcutsItemView.java | 62 ++++++++++++++----- 7 files changed, 116 insertions(+), 63 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f8c591c13d..3793bb5a06 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -732,6 +732,16 @@ public class LauncherModel extends BroadcastReceiver if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts"); bindDeepShortcuts(); + // Take a break + if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle"); + waitForIdle(); + verifyNotStopped(); + + // fourth step + if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets"); + refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */, + null /* packageUser */); + synchronized (mLock) { // Everything loaded bind the data. mModelLoaded = true; diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index ee7186aa42..f03c9c77ca 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -44,6 +44,7 @@ import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.ManagedProfileHeuristic; +import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; import java.util.Arrays; @@ -370,6 +371,14 @@ public class PackageUpdatedTask extends ExtendedModelTask { callbacks.notifyWidgetProvidersChanged(); } }); + } else if (Utilities.isAtLeastO() && mOp == OP_ADD) { + // Load widgets for the new package. + for (int i = 0; i < N; i++) { + LauncherModel model = app.getModel(); + model.refreshAndBindWidgetsAndShortcuts( + model.getCallback(), false /* bindFirst */, + new PackageUserKey(packages[i], mUser) /* packageUser */); + } } } } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 5891085e4b..a4823ae3e3 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -146,21 +146,24 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra return null; } - List shortcutIds = launcher.getPopupDataProvider().getShortcutIdsForItem(itemInfo); - List notificationKeys = launcher.getPopupDataProvider() + PopupDataProvider popupDataProvider = launcher.getPopupDataProvider(); + List shortcutIds = popupDataProvider.getShortcutIdsForItem(itemInfo); + List notificationKeys = popupDataProvider .getNotificationKeysForItem(itemInfo); + List systemShortcuts = popupDataProvider + .getEnabledSystemShortcutsForItem(itemInfo); final PopupContainerWithArrow container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate( R.layout.popup_container, launcher.getDragLayer(), false); container.setVisibility(View.INVISIBLE); launcher.getDragLayer().addView(container); - container.populateAndShow(icon, shortcutIds, notificationKeys); + container.populateAndShow(icon, shortcutIds, notificationKeys, systemShortcuts); return container; } public void populateAndShow(final BubbleTextView originalIcon, final List shortcutIds, - final List notificationKeys) { + final List notificationKeys, List systemShortcuts) { final Resources resources = getResources(); final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width); final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height); @@ -171,7 +174,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra // Add dummy views first, and populate with real info when ready. PopupPopulator.Item[] itemsToPopulate = PopupPopulator - .getItemsToPopulate(shortcutIds, notificationKeys); + .getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts); addDummyViews(originalIcon, itemsToPopulate, notificationKeys.size() > 1); measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); @@ -218,7 +221,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable( mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()), this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView, - systemShortcutViews)); + systemShortcuts, systemShortcutViews)); } private void addDummyViews(BubbleTextView originalIcon, @@ -624,12 +627,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra @Override protected void onWidgetsBound() { - enableWidgets(); - } - - public boolean enableWidgets() { - return mShortcutsItemView != null && mShortcutsItemView.enableWidgets( - (ItemInfo) mOriginalIcon.getTag()); + if (mShortcutsItemView != null) { + mShortcutsItemView.enableWidgetsIfExist(mOriginalIcon); + } } private ObjectAnimator createArrowScaleAnim(float scale) { diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index c1a9ba1bc6..eaceaa959b 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -33,6 +33,7 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -49,7 +50,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan private static final String TAG = "PopupDataProvider"; /** Note that these are in order of priority. */ - public static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] { + private static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] { new SystemShortcut.AppInfo(), new SystemShortcut.Widgets(), }; @@ -240,6 +241,16 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan : notificationListener.getNotificationsForKeys(notificationKeys); } + public @NonNull List getEnabledSystemShortcutsForItem(ItemInfo info) { + List systemShortcuts = new ArrayList<>(); + for (SystemShortcut systemShortcut : SYSTEM_SHORTCUTS) { + if (systemShortcut.getOnClickListener(mLauncher, info) != null) { + systemShortcuts.add(systemShortcut); + } + } + return systemShortcuts; + } + public void cancelNotification(String notificationKey) { NotificationListener notificationListener = NotificationListener.getInstanceIfConnected(); if (notificationListener == null) { diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java index 1128894748..c62d8771a5 100644 --- a/src/com/android/launcher3/popup/PopupPopulator.java +++ b/src/com/android/launcher3/popup/PopupPopulator.java @@ -72,7 +72,8 @@ public class PopupPopulator { } public static @NonNull Item[] getItemsToPopulate(@NonNull List shortcutIds, - @NonNull List notificationKeys) { + @NonNull List notificationKeys, + @NonNull List systemShortcuts) { boolean hasNotifications = notificationKeys.size() > 0; int numNotificationItems = hasNotifications ? 1 : 0; int numShortcuts = shortcutIds.size(); @@ -80,7 +81,7 @@ public class PopupPopulator { numShortcuts = MAX_SHORTCUTS_IF_NOTIFICATIONS; } int numItems = Math.min(MAX_ITEMS, numShortcuts + numNotificationItems) - + PopupDataProvider.SYSTEM_SHORTCUTS.length; + + systemShortcuts.size(); Item[] items = new Item[numItems]; for (int i = 0; i < numItems; i++) { items[i] = Item.SHORTCUT; @@ -91,7 +92,7 @@ public class PopupPopulator { } // The system shortcuts are always last. boolean iconsOnly = !shortcutIds.isEmpty(); - for (int i = 0; i < PopupDataProvider.SYSTEM_SHORTCUTS.length; i++) { + for (int i = 0; i < systemShortcuts.size(); i++) { items[numItems - 1 - i] = iconsOnly ? Item.SYSTEM_SHORTCUT_ICON : Item.SYSTEM_SHORTCUT; } return items; @@ -182,7 +183,8 @@ public class PopupPopulator { final Handler uiHandler, final PopupContainerWithArrow container, final List shortcutIds, final List shortcutViews, final List notificationKeys, - final NotificationItemView notificationView, final List systemShortcutViews) { + final NotificationItemView notificationView, final List systemShortcuts, + final List systemShortcutViews) { final ComponentName activity = originalInfo.getTargetComponent(); final UserHandle user = originalInfo.user; return new Runnable() { @@ -217,8 +219,8 @@ public class PopupPopulator { // This ensures that mLauncher.getWidgetsForPackageUser() // doesn't return null (it puts all the widgets in memory). - for (int i = 0; i < PopupDataProvider.SYSTEM_SHORTCUTS.length; i++) { - final SystemShortcut systemShortcut = PopupDataProvider.SYSTEM_SHORTCUTS[i]; + for (int i = 0; i < systemShortcuts.size(); i++) { + final SystemShortcut systemShortcut = systemShortcuts.get(i); uiHandler.post(new UpdateSystemShortcutChild(container, systemShortcutViews.get(i), systemShortcut, launcher, originalInfo)); } @@ -274,7 +276,6 @@ public class PopupPopulator { /** Updates the system shortcut child based on the given shortcut info. */ private static class UpdateSystemShortcutChild implements Runnable { - private static final float DISABLED_ALPHA = 0.38f; private final PopupContainerWithArrow mContainer; private final View mSystemShortcutChild; @@ -294,31 +295,26 @@ public class PopupPopulator { @Override public void run() { final Context context = mSystemShortcutChild.getContext(); - if (mSystemShortcutChild instanceof DeepShortcutView) { - // Expanded system shortcut, with both icon and text shown on white background. - final DeepShortcutView shortcutView = (DeepShortcutView) mSystemShortcutChild; - shortcutView.getIconView().setBackground(mSystemShortcutInfo.getIcon(context, - android.R.attr.textColorTertiary)); - shortcutView.getBubbleText().setText(mSystemShortcutInfo.getLabel(context)); - } else if (mSystemShortcutChild instanceof ImageView) { - // Only the system shortcut icon shows on a gray background header. - final ImageView shortcutIcon = (ImageView) mSystemShortcutChild; - shortcutIcon.setImageDrawable(mSystemShortcutInfo.getIcon(context, - android.R.attr.textColorHint)); - shortcutIcon.setContentDescription(mSystemShortcutInfo.getLabel(context)); - } - if (!(mSystemShortcutInfo instanceof SystemShortcut.Widgets)) { - mSystemShortcutChild.setOnClickListener(mSystemShortcutInfo - .getOnClickListener(mLauncher, mItemInfo)); - } else { - mSystemShortcutChild.setTag(mSystemShortcutInfo); - // Widgets might not be enabled right away. - if (mContainer.enableWidgets()) { - return; - } - // Disable Widgets (we might be able to re-enable when widgets are bound). - mSystemShortcutChild.setAlpha(DISABLED_ALPHA); - } + initializeSystemShortcut(context, mSystemShortcutChild, mSystemShortcutInfo); + mSystemShortcutChild.setOnClickListener(mSystemShortcutInfo + .getOnClickListener(mLauncher, mItemInfo)); } } + + public static void initializeSystemShortcut(Context context, View view, SystemShortcut info) { + if (view instanceof DeepShortcutView) { + // Expanded system shortcut, with both icon and text shown on white background. + final DeepShortcutView shortcutView = (DeepShortcutView) view; + shortcutView.getIconView().setBackground(info.getIcon(context, + android.R.attr.textColorTertiary)); + shortcutView.getBubbleText().setText(info.getLabel(context)); + } else if (view instanceof ImageView) { + // Only the system shortcut icon shows on a gray background header. + final ImageView shortcutIcon = (ImageView) view; + shortcutIcon.setImageDrawable(info.getIcon(context, + android.R.attr.textColorHint)); + shortcutIcon.setContentDescription(info.getLabel(context)); + } + view.setTag(info); + } } diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index de884996ba..15fd126097 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -43,7 +43,6 @@ public abstract class SystemShortcut { public abstract View.OnClickListener getOnClickListener(final Launcher launcher, final ItemInfo itemInfo); - public static class Widgets extends SystemShortcut { public Widgets() { diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java index 1f90bb0238..ee64b984a7 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java +++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java @@ -26,6 +26,7 @@ import android.view.View; import android.widget.LinearLayout; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils; @@ -119,6 +120,10 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick } public void addShortcutView(View shortcutView, PopupPopulator.Item shortcutType) { + addShortcutView(shortcutView, shortcutType, -1); + } + + private void addShortcutView(View shortcutView, PopupPopulator.Item shortcutType, int index) { if (shortcutType == PopupPopulator.Item.SHORTCUT) { mDeepShortcutViews.add((DeepShortcutView) shortcutView); } else { @@ -131,7 +136,7 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick R.layout.system_shortcut_icons, mShortcutsLayout, false); mShortcutsLayout.addView(mSystemShortcutIcons, 0); } - mSystemShortcutIcons.addView(shortcutView); + mSystemShortcutIcons.addView(shortcutView, index); } else { if (mShortcutsLayout.getChildCount() > 0) { View prevChild = mShortcutsLayout.getChildAt(mShortcutsLayout.getChildCount() - 1); @@ -139,7 +144,7 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick prevChild.findViewById(R.id.divider).setVisibility(VISIBLE); } } - mShortcutsLayout.addView(shortcutView); + mShortcutsLayout.addView(shortcutView, index); } } @@ -160,24 +165,47 @@ public class ShortcutsItemView extends PopupItemView implements View.OnLongClick } /** - * Sets the onClickListener on widgets system shortcut child, and updates alpha to 1. - * @return whether widgets is enabled, i.e. the onClickListener is not null. + * Adds a {@link SystemShortcut.Widgets} item if there are widgets for the given ItemInfo. */ - public boolean enableWidgets(ItemInfo itemInfo) { - for (View systemShortcut : mSystemShortcutViews) { - if (systemShortcut.getTag() instanceof SystemShortcut.Widgets) { - View.OnClickListener onClickListener = - ((SystemShortcut.Widgets) systemShortcut.getTag()).getOnClickListener( - mLauncher, itemInfo); - if (onClickListener != null) { - systemShortcut.setAlpha(1f); - systemShortcut.setOnClickListener(onClickListener); - return true; - } - return false; + public void enableWidgetsIfExist(final BubbleTextView originalIcon) { + ItemInfo itemInfo = (ItemInfo) originalIcon.getTag(); + SystemShortcut widgetInfo = new SystemShortcut.Widgets(); + View.OnClickListener onClickListener = widgetInfo.getOnClickListener(mLauncher, itemInfo); + View widgetsView = null; + for (View systemShortcutView : mSystemShortcutViews) { + if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) { + widgetsView = systemShortcutView; + break; + } + } + final PopupPopulator.Item widgetsItem = mSystemShortcutIcons == null + ? PopupPopulator.Item.SYSTEM_SHORTCUT + : PopupPopulator.Item.SYSTEM_SHORTCUT_ICON; + if (onClickListener != null && widgetsView == null) { + // We didn't have any widgets cached but now there are some, so enable the shortcut. + widgetsView = mLauncher.getLayoutInflater().inflate(widgetsItem.layoutId, this, false); + PopupPopulator.initializeSystemShortcut(getContext(), widgetsView, widgetInfo); + widgetsView.setOnClickListener(onClickListener); + if (widgetsItem == PopupPopulator.Item.SYSTEM_SHORTCUT_ICON) { + addShortcutView(widgetsView, widgetsItem, 0); + } else { + // If using the expanded system shortcut (as opposed to just the icon), we need to + // reopen the container to ensure measurements etc. all work out. While this could + // be quite janky, in practice the user would typically see a small flicker as the + // animation restarts partway through, and this is a very rare edge case anyway. + ((PopupContainerWithArrow) getParent()).close(false); + PopupContainerWithArrow.showForIcon(originalIcon); + } + } else if (onClickListener == null && widgetsView != null) { + // No widgets exist, but we previously added the shortcut so remove it. + if (widgetsItem == PopupPopulator.Item.SYSTEM_SHORTCUT_ICON) { + mSystemShortcutViews.remove(widgetsView); + mSystemShortcutIcons.removeView(widgetsView); + } else { + ((PopupContainerWithArrow) getParent()).close(false); + PopupContainerWithArrow.showForIcon(originalIcon); } } - return false; } @Override