Merge "Fixing loadWorkspace" into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 974b0df..5588289 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -76,11 +76,20 @@
android:process=":wallpaper_chooser">
</service>
+ <service android:name="com.android.launcher3.badging.NotificationListener"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
+
<meta-data android:name="android.nfc.disable_beam_default"
android:value="true" />
<activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:autoRemoveFromRecents="true"
android:label="@string/action_add_to_workspace" >
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_ITEM" />
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java
deleted file mode 100644
index 052e5d0..0000000
--- a/src/com/android/launcher3/AnotherWindowDropTarget.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2015 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.Context;
-import android.graphics.PointF;
-import android.graphics.Rect;
-
-/**
- * Drop target used when another window (i.e. another process) has accepted a global system drag.
- * If the accepted item was a shortcut, we delete it from Launcher.
- */
-public class AnotherWindowDropTarget implements DropTarget {
- final Launcher mLauncher;
-
- public AnotherWindowDropTarget (Context context) { mLauncher = Launcher.getLauncher(context); }
-
- @Override
- public boolean isDropEnabled() { return true; }
-
- @Override
- public void onDrop(DragObject dragObject) {
- dragObject.deferDragViewCleanupPostAnimation = false;
- LauncherModel.deleteItemFromDatabase(mLauncher, (ShortcutInfo) dragObject.dragInfo);
- }
-
- @Override
- public void onDragEnter(DragObject dragObject) {}
-
- @Override
- public void onDragOver(DragObject dragObject) {}
-
- @Override
- public void onDragExit(DragObject dragObject) {}
-
- @Override
- public boolean acceptDrop(DragObject dragObject) {
- return dragObject.dragInfo instanceof ShortcutInfo;
- }
-
- @Override
- public void prepareAccessibilityDrop() {}
-
- // These methods are implemented in Views
- @Override
- public void getHitRectRelativeToDragLayer(Rect outRect) {}
-}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d9e9c7b..47a5b4f 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -39,14 +39,16 @@
import com.android.launcher3.IconCache.IconLoadRequest;
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
-import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.badging.NotificationInfo;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.model.PackageItemInfo;
import java.text.NumberFormat;
+import java.util.List;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -168,6 +170,8 @@
if (promiseStateChanged || info.isPromise()) {
applyPromiseState(promiseStateChanged);
}
+
+ applyBadgeState(info);
}
public void applyFromApplicationInfo(AppInfo info) {
@@ -178,6 +182,8 @@
// Verify high res immediately
verifyHighRes();
+
+ applyBadgeState(info);
}
public void applyFromPackageItemInfo(PackageItemInfo info) {
@@ -502,8 +508,9 @@
}
}
- public void applyBadgeState(BadgeInfo badgeInfo) {
+ public void applyBadgeState(ItemInfo itemInfo) {
if (mIcon instanceof FastBitmapDrawable) {
+ BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
BadgeRenderer badgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer);
}
@@ -634,7 +641,8 @@
* Returns true if the view can show custom shortcuts.
*/
public boolean hasDeepShortcuts() {
- return !mLauncher.getShortcutIdsForItem((ItemInfo) getTag()).isEmpty();
+ return !mLauncher.getPopupDataProvider().getShortcutIdsForItem((ItemInfo) getTag())
+ .isEmpty();
}
/**
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index b3e59f9..587d445 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -30,12 +30,13 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.util.Log;
import android.util.SparseArray;
import android.view.animation.DecelerateInterpolator;
-import com.android.launcher3.graphics.IconPalette;
-import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.graphics.IconPalette;
public class FastBitmapDrawable extends Drawable {
private static final float DISABLED_DESATURATION = 1f;
@@ -123,13 +124,17 @@
}
public void applyIconBadge(BadgeInfo badgeInfo, BadgeRenderer badgeRenderer) {
+ boolean wasBadged = mBadgeInfo != null;
+ boolean isBadged = badgeInfo != null;
mBadgeInfo = badgeInfo;
mBadgeRenderer = badgeRenderer;
- if (mIconPalette == null) {
- mIconPalette = IconPalette.fromDominantColor(Utilities
- .findDominantColorByHue(mBitmap, 20));
+ if (wasBadged || isBadged) {
+ if (mBadgeInfo != null && mIconPalette == null) {
+ mIconPalette = IconPalette.fromDominantColor(Utilities
+ .findDominantColorByHue(mBitmap, 20));
+ }
+ invalidateSelf();
}
- invalidateSelf();
}
@Override
@@ -157,7 +162,7 @@
}
private boolean hasBadge() {
- return mBadgeInfo != null && mBadgeInfo.getNotificationCount() != null;
+ return mBadgeInfo != null && mBadgeInfo.getNotificationCount() != 0;
}
@Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8d8a70c..26e388d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -49,6 +49,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -84,6 +85,8 @@
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.badging.NotificationListener;
+import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PinItemRequestCompat;
@@ -94,6 +97,7 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.PinItemDragListener;
import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -117,6 +121,7 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
@@ -131,10 +136,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Default launcher application.
@@ -261,8 +266,7 @@
private boolean mHasFocus = false;
private boolean mAttached = false;
- /** Maps launcher activity components to their list of shortcut ids. */
- private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
+ private PopupDataProvider mPopupDataProvider;
private View.OnTouchListener mHapticFeedbackTouchListener;
@@ -395,6 +399,8 @@
mExtractedColors = new ExtractedColors();
loadExtractedColorsAndColorItems();
+ mPopupDataProvider = new PopupDataProvider(this);
+
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.addAccessibilityStateChangeListener(this);
@@ -653,6 +659,10 @@
return (int) info.id;
}
+ public PopupDataProvider getPopupDataProvider() {
+ return mPopupDataProvider;
+ }
+
/**
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
@@ -826,7 +836,7 @@
}
@Override
- protected void onActivityResult(
+ public void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
handleActivityResult(requestCode, resultCode, data);
if (mLauncherCallbacks != null) {
@@ -928,6 +938,8 @@
if (Utilities.ATLEAST_NOUGAT_MR1) {
mAppWidgetHost.stopListening();
}
+
+ NotificationListener.removeNotificationsChangedListener();
}
@Override
@@ -942,6 +954,10 @@
if (Utilities.ATLEAST_NOUGAT_MR1) {
mAppWidgetHost.startListening();
}
+
+ if (!isWorkspaceLoading()) {
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+ }
}
@Override
@@ -1566,6 +1582,19 @@
}
};
+ public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ mWorkspace.updateIconBadges(updatedBadges);
+ mAppsView.updateIconBadges(updatedBadges);
+ }
+ };
+ if (!waitUntilResume(r)) {
+ r.run();
+ }
+ }
+
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -1752,6 +1781,14 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent();
}
+
+ Parcelable dragExtra = intent
+ .getParcelableExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER);
+ if (dragExtra instanceof PinItemDragListener) {
+ PinItemDragListener dragListener = (PinItemDragListener) dragExtra;
+ dragListener.setLauncher(this);
+ mDragLayer.setOnDragListener(dragListener);
+ }
}
if (mLauncherCallbacks != null) {
@@ -3646,6 +3683,8 @@
InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.finishBindingItems(false);
}
@@ -3715,21 +3754,7 @@
*/
@Override
public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
- mDeepShortcutMap = deepShortcutMapCopy;
- if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
- }
-
- public List<String> getShortcutIdsForItem(ItemInfo info) {
- if (!DeepShortcutManager.supportsShortcuts(info)) {
- return Collections.EMPTY_LIST;
- }
- ComponentName component = info.getTargetComponent();
- if (component == null) {
- return Collections.EMPTY_LIST;
- }
-
- List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
- return ids == null ? Collections.EMPTY_LIST : ids;
+ mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
}
/**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6eb87f2..e646dd9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -78,6 +78,7 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.MultiStateAlphaController;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
@@ -86,6 +87,7 @@
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Set;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -3957,6 +3959,23 @@
});
}
+ public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
+ final PackageUserKey packageUserKey = new PackageUserKey(null, null);
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v) {
+ if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+ packageUserKey.updateFromItemInfo(info);
+ if (updatedBadges.contains(packageUserKey)) {
+ ((BubbleTextView) v).applyBadgeState(info);
+ }
+ }
+ // process all the shortcuts
+ return false;
+ }
+ });
+ }
+
public void removeAbandonedPromise(String packageName, UserHandle user) {
HashSet<String> packages = new HashSet<>(1);
packages.add(packageName);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index a2266fe..ec1fa34 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -53,9 +53,11 @@
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* The all apps view container.
@@ -472,4 +474,16 @@
navBarBg.setVisibility(View.VISIBLE);
}
}
+
+ public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
+ final PackageUserKey packageUserKey = new PackageUserKey(null, null);
+ for (AlphabeticalAppsList.AdapterItem app : mApps.getAdapterItems()) {
+ if (app.appInfo != null) {
+ packageUserKey.updateFromItemInfo(app.appInfo);
+ if (updatedBadges.contains(packageUserKey)) {
+ mAdapter.notifyItemChanged(app.position);
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/badge/BadgeInfo.java b/src/com/android/launcher3/badge/BadgeInfo.java
index 0a9f87c..98d2277 100644
--- a/src/com/android/launcher3/badge/BadgeInfo.java
+++ b/src/com/android/launcher3/badge/BadgeInfo.java
@@ -16,18 +16,60 @@
package com.android.launcher3.badge;
+import com.android.launcher3.util.PackageUserKey;
+
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Contains data to be used in an icon badge.
*/
public class BadgeInfo {
- private int mNotificationCount;
+ /** Used to link this BadgeInfo to icons on the workspace and all apps */
+ private PackageUserKey mPackageUserKey;
+ /**
+ * The keys of the notifications that this badge represents. These keys can later be
+ * used to retrieve {@link com.android.launcher3.badging.NotificationInfo}'s.
+ */
+ private Set<String> mNotificationKeys;
- public void setNotificationCount(int count) {
- mNotificationCount = count;
+ public BadgeInfo(PackageUserKey packageUserKey) {
+ mPackageUserKey = packageUserKey;
+ mNotificationKeys = new HashSet<>();
}
- public String getNotificationCount() {
- return mNotificationCount == 0 ? null : String.valueOf(mNotificationCount);
+ /**
+ * Returns whether the notification was added (false if it already existed).
+ */
+ public boolean addNotificationKey(String notificationKey) {
+ return mNotificationKeys.add(notificationKey);
+ }
+
+ /**
+ * Returns whether the notification was removed (false if it didn't exist).
+ */
+ public boolean removeNotificationKey(String notificationKey) {
+ return mNotificationKeys.remove(notificationKey);
+ }
+
+ public Set<String> getNotificationKeys() {
+ return mNotificationKeys;
+ }
+
+ public int getNotificationCount() {
+ return mNotificationKeys.size();
+ }
+
+ /**
+ * Whether newBadge represents the same PackageUserKey as this badge, and icons with
+ * this badge should be invalidated. So, for instance, if a badge has 3 notifications
+ * and one of those notifications is updated, this method should return false because
+ * the badge still says "3" and the contents of those notifications are only retrieved
+ * upon long-click. This method always returns true when adding or removing notifications.
+ */
+ public boolean shouldBeInvalidated(BadgeInfo newBadge) {
+ return mPackageUserKey.equals(newBadge.mPackageUserKey)
+ && getNotificationCount() != newBadge.getNotificationCount();
}
}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 238b918..787ee72 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -61,7 +61,7 @@
mBackgroundRect.set(iconBounds.right - size, iconBounds.top, iconBounds.right,
iconBounds.top + size);
canvas.drawOval(mBackgroundRect, mBackgroundPaint);
- String notificationCount = badgeInfo.getNotificationCount();
+ String notificationCount = String.valueOf(badgeInfo.getNotificationCount());
canvas.drawText(notificationCount,
mBackgroundRect.centerX(),
mBackgroundRect.centerY() + mTextHeight / 2,
diff --git a/src/com/android/launcher3/badging/NotificationInfo.java b/src/com/android/launcher3/badging/NotificationInfo.java
new file mode 100644
index 0000000..2590add
--- /dev/null
+++ b/src/com/android/launcher3/badging/NotificationInfo.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.badging;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.service.notification.StatusBarNotification;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.util.PackageUserKey;
+
+/**
+ * An object that contains relevant information from a {@link StatusBarNotification}. This should
+ * only be created when we need to show the notification contents on the UI; until then, a
+ * {@link com.android.launcher3.badge.BadgeInfo} with only the notification key should
+ * be passed around, and then this can be constructed using the StatusBarNotification from
+ * {@link NotificationListener#getNotificationsForKeys(String[])}.
+ */
+public class NotificationInfo implements View.OnClickListener {
+
+ public final PackageUserKey packageUserKey;
+ public final String notificationKey;
+ public final CharSequence title;
+ public final CharSequence text;
+ public final Drawable iconDrawable;
+ public final PendingIntent intent;
+ public final boolean autoCancel;
+
+ /**
+ * Extracts the data that we need from the StatusBarNotification.
+ */
+ public NotificationInfo(Context context, StatusBarNotification notification) {
+ packageUserKey = PackageUserKey.fromNotification(notification);
+ notificationKey = notification.getKey();
+ title = notification.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE);
+ text = notification.getNotification().extras.getCharSequence(Notification.EXTRA_TEXT);
+ Icon icon = notification.getNotification().getLargeIcon();
+ if (icon == null) {
+ icon = notification.getNotification().getSmallIcon();
+ iconDrawable = icon.loadDrawable(context);
+ iconDrawable.setTint(notification.getNotification().color);
+ } else {
+ iconDrawable = icon.loadDrawable(context);
+ }
+ intent = notification.getNotification().contentIntent;
+ autoCancel = (notification.getNotification().flags
+ & Notification.FLAG_AUTO_CANCEL) != 0;
+ }
+
+ @Override
+ public void onClick(View view) {
+ final Launcher launcher = Launcher.getLauncher(view.getContext());
+ try {
+ intent.send();
+ } catch (PendingIntent.CanceledException e) {
+ e.printStackTrace();
+ }
+ if (autoCancel) {
+ launcher.getPopupDataProvider().cancelNotification(notificationKey);
+ }
+ DeepShortcutsContainer.getOpen(launcher).close(true);
+ }
+}
diff --git a/src/com/android/launcher3/badging/NotificationListener.java b/src/com/android/launcher3/badging/NotificationListener.java
new file mode 100644
index 0000000..0a85d56
--- /dev/null
+++ b/src/com/android/launcher3/badging/NotificationListener.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 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.badging;
+
+import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.annotation.Nullable;
+import android.support.v4.util.Pair;
+
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.PackageUserKey;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A {@link NotificationListenerService} that sends updates to its
+ * {@link NotificationsChangedListener} when notifications are posted or canceled,
+ * as well and when this service first connects. An instance of NotificationListener,
+ * and its methods for getting notifications, can be obtained via {@link #getInstance()}.
+ */
+public class NotificationListener extends NotificationListenerService {
+
+ private static final int MSG_NOTIFICATION_POSTED = 1;
+ private static final int MSG_NOTIFICATION_REMOVED = 2;
+ private static final int MSG_NOTIFICATION_FULL_REFRESH = 3;
+
+ private static NotificationListener sNotificationListenerInstance = null;
+ private static NotificationsChangedListener sNotificationsChangedListener;
+
+ private final Handler mWorkerHandler;
+ private final Handler mUiHandler;
+
+ private Handler.Callback mWorkerCallback = new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFICATION_POSTED:
+ mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
+ break;
+ case MSG_NOTIFICATION_REMOVED:
+ mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
+ break;
+ case MSG_NOTIFICATION_FULL_REFRESH:
+ final List<StatusBarNotification> activeNotifications
+ = filterNotifications(getActiveNotifications());
+ mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
+ break;
+ }
+ return true;
+ }
+ };
+
+ private Handler.Callback mUiCallback = new Handler.Callback() {
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_NOTIFICATION_POSTED:
+ if (sNotificationsChangedListener != null) {
+ Pair<PackageUserKey, String> pair
+ = (Pair<PackageUserKey, String>) message.obj;
+ sNotificationsChangedListener.onNotificationPosted(pair.first, pair.second);
+ }
+ break;
+ case MSG_NOTIFICATION_REMOVED:
+ if (sNotificationsChangedListener != null) {
+ Pair<PackageUserKey, String> pair
+ = (Pair<PackageUserKey, String>) message.obj;
+ sNotificationsChangedListener.onNotificationRemoved(pair.first, pair.second);
+ }
+ break;
+ case MSG_NOTIFICATION_FULL_REFRESH:
+ if (sNotificationsChangedListener != null) {
+ sNotificationsChangedListener.onNotificationFullRefresh(
+ (List<StatusBarNotification>) message.obj);
+ }
+ break;
+ }
+ return true;
+ }
+ };
+
+ public NotificationListener() {
+ super();
+ mWorkerHandler = new Handler(LauncherModel.getWorkerLooper(), mWorkerCallback);
+ mUiHandler = new Handler(Looper.getMainLooper(), mUiCallback);
+ }
+
+ public static @Nullable NotificationListener getInstance() {
+ return sNotificationListenerInstance;
+ }
+
+ public static void setNotificationsChangedListener(NotificationsChangedListener listener) {
+ if (!FeatureFlags.BADGE_ICONS) {
+ return;
+ }
+ sNotificationsChangedListener = listener;
+
+ NotificationListener notificationListener = getInstance();
+ if (notificationListener != null) {
+ notificationListener.onNotificationFullRefresh();
+ }
+ }
+
+ public static void removeNotificationsChangedListener() {
+ sNotificationsChangedListener = null;
+ }
+
+ @Override
+ public void onListenerConnected() {
+ super.onListenerConnected();
+ sNotificationListenerInstance = this;
+ onNotificationFullRefresh();
+ }
+
+ private void onNotificationFullRefresh() {
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_FULL_REFRESH).sendToTarget();
+ }
+
+ @Override
+ public void onListenerDisconnected() {
+ super.onListenerDisconnected();
+ sNotificationListenerInstance = null;
+ }
+
+ @Override
+ public void onNotificationPosted(final StatusBarNotification sbn) {
+ super.onNotificationPosted(sbn);
+ if (!shouldBeFilteredOut(sbn.getNotification())) {
+ Pair<PackageUserKey, String> packageUserKeyAndNotificationKey
+ = new Pair<>(PackageUserKey.fromNotification(sbn), sbn.getKey());
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, packageUserKeyAndNotificationKey)
+ .sendToTarget();
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(final StatusBarNotification sbn) {
+ super.onNotificationRemoved(sbn);
+ if (!shouldBeFilteredOut(sbn.getNotification())) {
+ Pair<PackageUserKey, String> packageUserKeyAndNotificationKey
+ = new Pair<>(PackageUserKey.fromNotification(sbn), sbn.getKey());
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
+ .sendToTarget();
+ }
+ }
+
+ /** This makes a potentially expensive binder call and should be run on a background thread. */
+ public List<StatusBarNotification> getNotificationsForKeys(String[] keys) {
+ StatusBarNotification[] notifications = NotificationListener.this
+ .getActiveNotifications(keys);
+ return notifications == null ? Collections.EMPTY_LIST : Arrays.asList(notifications);
+ }
+
+ /**
+ * Filter out notifications that don't have an intent
+ * or are headers for grouped notifications.
+ *
+ * TODO: use the system concept of a badged notification instead
+ */
+ private List<StatusBarNotification> filterNotifications(
+ StatusBarNotification[] notifications) {
+ if (notifications == null) return null;
+ Set<Integer> removedNotifications = new HashSet<>();
+ for (int i = 0; i < notifications.length; i++) {
+ if (shouldBeFilteredOut(notifications[i].getNotification())) {
+ removedNotifications.add(i);
+ }
+ }
+ List<StatusBarNotification> filteredNotifications = new ArrayList<>(
+ notifications.length - removedNotifications.size());
+ for (int i = 0; i < notifications.length; i++) {
+ if (!removedNotifications.contains(i)) {
+ filteredNotifications.add(notifications[i]);
+ }
+ }
+ return filteredNotifications;
+ }
+
+ private boolean shouldBeFilteredOut(Notification notification) {
+ boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+ return (notification.contentIntent == null || isGroupHeader);
+ }
+
+ public interface NotificationsChangedListener {
+ void onNotificationPosted(PackageUserKey postedPackageUserKey, String notificationKey);
+ void onNotificationRemoved(PackageUserKey removedPackageUserKey, String notificationKey);
+ void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications);
+ }
+}
diff --git a/src/com/android/launcher3/compat/PinItemRequestCompat.java b/src/com/android/launcher3/compat/PinItemRequestCompat.java
index 74d7765..481310a 100644
--- a/src/com/android/launcher3/compat/PinItemRequestCompat.java
+++ b/src/com/android/launcher3/compat/PinItemRequestCompat.java
@@ -21,13 +21,14 @@
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
+import android.os.Parcel;
import android.os.Parcelable;
/**
* A wrapper around platform implementation of PinItemRequestCompat until the
* updated SDK is available.
*/
-public class PinItemRequestCompat {
+public class PinItemRequestCompat implements Parcelable {
public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
@@ -83,6 +84,32 @@
}
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeParcelable(mObject, i);
+ }
+
+ public Intent toIntent() {
+ return new Intent().putExtra(EXTRA_PIN_ITEM_REQUEST, mObject);
+ }
+
+ public static final Parcelable.Creator<PinItemRequestCompat> CREATOR =
+ new Parcelable.Creator<PinItemRequestCompat>() {
+ public PinItemRequestCompat createFromParcel(Parcel source) {
+ Parcelable object = source.readParcelable(null);
+ return new PinItemRequestCompat(object);
+ }
+
+ public PinItemRequestCompat[] newArray(int size) {
+ return new PinItemRequestCompat[size];
+ }
+ };
+
public static PinItemRequestCompat getPinItemRequest(Intent intent) {
Parcelable extra = intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
return extra == null ? null : new PinItemRequestCompat(extra);
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
index ece7759..1cfbd20 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
@@ -35,6 +35,7 @@
import android.widget.Toast;
import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -67,7 +68,7 @@
public abstract Drawable getFullResIcon(IconCache cache);
- public boolean startConfigActivity(Activity activity, int requestCode) {
+ public boolean startConfigActivity(Launcher activity, int requestCode) {
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
.setComponent(getComponent());
try {
@@ -136,7 +137,7 @@
}
@Override
- public boolean startConfigActivity(Activity activity, int requestCode) {
+ public boolean startConfigActivity(Launcher activity, int requestCode) {
if (getUser().equals(Process.myUserHandle())) {
return super.startConfigActivity(activity, requestCode);
}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 6c6f141..423fdab 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -17,12 +17,23 @@
package com.android.launcher3.dragndrop;
import android.annotation.TargetApi;
+import android.app.ActivityOptions;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.View.*;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InstallShortcutReceiver;
@@ -38,13 +49,18 @@
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetHostViewLoader;
+import com.android.launcher3.widget.WidgetImageView;
@TargetApi(Build.VERSION_CODES.N_MR1)
-public class AddItemActivity extends BaseActivity {
+public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
+
+ private static final int SHADOW_SIZE = 10;
private static final int REQUEST_BIND_APPWIDGET = 1;
private static final String STATE_EXTRA_WIDGET_ID = "state.widget.id";
+ private final PointF mLastTouchPos = new PointF();
+
private PinItemRequestCompat mRequest;
private LauncherAppState mApp;
private InvariantDeviceProfile mIdp;
@@ -86,11 +102,54 @@
finish();
}
}
+
+ mWidgetCell.setOnTouchListener(this);
+ mWidgetCell.setOnLongClickListener(this);
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ mLastTouchPos.set(motionEvent.getX(), motionEvent.getY());
+ return false;
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ // Find the position of the preview relative to the touch location.
+ WidgetImageView img = mWidgetCell.getWidgetView();
+ Rect bounds = img.getBitmapBounds();
+ bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y);
+
+ // Start home and pass the draw request params
+ PinItemDragListener listener = new PinItemDragListener(mRequest, bounds);
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(getPackageName())
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER, listener);
+ startActivity(homeIntent,
+ ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+
+ // Start a system drag and drop. We use a transparent bitmap as preview for system drag
+ // as the preview is handled internally by launcher.
+ ClipDescription description = new ClipDescription("", new String[]{listener.getMimeType()});
+ ClipData data = new ClipData(description, new ClipData.Item(""));
+ view.startDragAndDrop(data, new DragShadowBuilder(view) {
+
+ @Override
+ public void onDrawShadow(Canvas canvas) { }
+
+ @Override
+ public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
+ outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE);
+ outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
+ }
+ }, null, View.DRAG_FLAG_GLOBAL);
+ return false;
}
private void setupShortcut() {
- WidgetItem item = new WidgetItem(new PinShortcutRequestActivityInfo(
- mRequest.getShortcutInfo(), this));
+ WidgetItem item = new WidgetItem(new PinShortcutRequestActivityInfo(mRequest, this));
mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
mWidgetCell.ensurePreview();
}
diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
deleted file mode 100644
index 1623010..0000000
--- a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2016 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.dragndrop;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-
-/**
- * DragSource used when the drag started at another window.
- */
-public class AnotherWindowDragSource implements DragSource {
-
- private final Context mContext;
-
- AnotherWindowDragSource(Context context) {
- mContext = context;
- }
-
- @Override
- public boolean supportsAppInfoDropTarget() {
- return false;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return false;
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 1;
- }
-
- @Override
- public void onDropCompleted(View target, DragObject d,
- boolean isFlingToDelete, boolean success) {
- if (!success) {
- Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null);
- }
-
- }
-
- @Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- // TODO: Probably log something
- }
-}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 745776d..80c2860 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -36,7 +36,6 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
@@ -447,7 +446,8 @@
/**
* Call this from a drag source view.
*/
- public boolean onDragEvent(DragEvent event) {
+ public boolean onDragEvent(long dragStartTime, DragEvent event) {
+ mFlingToDeleteHelper.recordDragEvent(dragStartTime, event);
return mDragDriver != null && mDragDriver.onDragEvent(event);
}
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index a90cfff..65c0f29 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -16,20 +16,13 @@
package com.android.launcher3.dragndrop;
-import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.Context;
-import android.content.Intent;
import android.view.DragEvent;
import android.view.MotionEvent;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.InstallShortcutReceiver;
-import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import java.util.ArrayList;
-
/**
* Base class for driving a drag/drop operation.
*/
@@ -107,7 +100,6 @@
private final DragObject mDragObject;
private final Context mContext;
- boolean mReceivedDropEvent = false;
float mLastX = 0;
float mLastY = 0;
@@ -149,65 +141,21 @@
case DragEvent.ACTION_DROP:
mLastX = event.getX();
mLastY = event.getY();
- mReceivedDropEvent =
- updateInfoFromClipData(event.getClipData(), event.getClipDescription());
- return mReceivedDropEvent;
-
+ mEventListener.onDriverDragMove(event.getX(), event.getY());
+ mEventListener.onDriverDragEnd(mLastX, mLastY);
+ return true;
case DragEvent.ACTION_DRAG_EXITED:
mEventListener.onDriverDragExitWindow();
return true;
case DragEvent.ACTION_DRAG_ENDED:
- if (mReceivedDropEvent) {
- mEventListener.onDriverDragEnd(mLastX, mLastY);
- } else {
- mEventListener.onDriverDragCancel();
- }
+ mEventListener.onDriverDragCancel();
return true;
default:
return false;
}
}
-
- private boolean updateInfoFromClipData(ClipData data, ClipDescription desc) {
- if (data == null) {
- return false;
- }
- ArrayList<Intent> intents = new ArrayList<>();
- int itemCount = data.getItemCount();
- for (int i = 0; i < itemCount; i++) {
- Intent intent = data.getItemAt(i).getIntent();
- if (intent == null) {
- continue;
- }
-
- // Give preference to shortcut intents.
- if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) {
- intents.add(intent);
- continue;
- }
- ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, intent);
- if (info != null) {
- mDragObject.dragInfo = info;
- return true;
- }
- return true;
- }
-
- // Try creating shortcuts just using the intent and label
- Intent fullIntent = new Intent().putExtra(Intent.EXTRA_SHORTCUT_NAME, desc.getLabel());
- for (Intent intent : intents) {
- fullIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
- ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, fullIntent);
- if (info != null) {
- mDragObject.dragInfo = info;
- return true;
- }
- }
-
- return false;
- }
}
/**
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 4279cc3..de416e3 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -21,21 +21,13 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.content.ClipDescription;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Build;
import android.util.AttributeSet;
-import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -59,9 +51,7 @@
import com.android.launcher3.PinchToOverviewListener;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
@@ -373,49 +363,6 @@
return false;
}
- @TargetApi(Build.VERSION_CODES.N)
- private void handleSystemDragStart(DragEvent event) {
- if (!FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER || !Utilities.ATLEAST_NOUGAT) {
- return;
- }
- if (mLauncher.isWorkspaceLocked()) {
- return;
- }
-
- ClipDescription description = event.getClipDescription();
- if (!description.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
- return;
- }
- ShortcutInfo info = new ShortcutInfo();
- // Set a dummy intent until we get the final value
- info.intent = new Intent();
-
- // Since we are not going through the workspace for starting the drag, set drag related
- // information on the workspace before starting the drag.
- ExternalDragPreviewProvider previewProvider =
- new ExternalDragPreviewProvider(mLauncher, info);
- mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
-
- DragOptions options = new DragOptions();
- options.systemDndStartPoint = new Point((int) event.getX(), (int) event.getY());
-
- int halfPadding = previewProvider.previewPadding / 2;
- mDragController.startDrag(
- Bitmap.createBitmap(1, 1, Config.ARGB_8888),
- 0, 0,
- new AnotherWindowDragSource(mLauncher), info,
- new Point(- halfPadding, halfPadding),
- previewProvider.getPreviewBounds(), 1f, options);
- }
-
- @Override
- public boolean onDragEvent (DragEvent event) {
- if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
- handleSystemDragStart(event);
- }
- return mDragController.onDragEvent(event);
- }
-
/**
* Determine the rect of the descendant in this DragLayer's coordinates
*
diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
deleted file mode 100644
index e558487..0000000
--- a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 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.dragndrop;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
-
-/**
- * Extension of {@link DragPreviewProvider} which provides a dummy outline when drag starts from
- * a different window.
- * It just draws an empty circle to a placeholder outline.
- */
-public class ExternalDragPreviewProvider extends DragPreviewProvider {
-
- private final Launcher mLauncher;
- private final ItemInfo mAddInfo;
-
- private final int[] mOutlineSize;
-
- public ExternalDragPreviewProvider(Launcher launcher, ItemInfo addInfo) {
- super(null, launcher);
- mLauncher = launcher;
- mAddInfo = addInfo;
-
- mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false, false);
- }
-
- public Rect getPreviewBounds() {
- Rect rect = new Rect();
- DeviceProfile dp = mLauncher.getDeviceProfile();
- rect.left = blurSizeOutline / 2;
- rect.top = (mOutlineSize[1] - dp.cellHeightPx) / 2;
- rect.right = rect.left + dp.iconSizePx;
- rect.bottom = rect.top + dp.iconSizePx;
- return rect;
- }
-
- @Override
- public Bitmap createDragOutline(Canvas canvas) {
- final Bitmap b = Bitmap.createBitmap(mOutlineSize[0], mOutlineSize[1], Bitmap.Config.ALPHA_8);
- canvas.setBitmap(b);
-
- Paint paint = new Paint();
- paint.setColor(Color.WHITE);
- paint.setStyle(Paint.Style.FILL);
-
- // Use 0.9f times the radius for the actual circle to account for icon normalization.
- float radius = getPreviewBounds().width() * 0.5f;
- canvas.drawCircle(blurSizeOutline / 2 + radius,
- blurSizeOutline / 2 + radius, radius * 0.9f, paint);
-
- HolographicOutlineHelper.getInstance(mLauncher).applyExpensiveOutlineWithBlur(b, canvas);
- canvas.setBitmap(null);
- return b;
- }
-}
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index a2aa27d..e794744 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -17,6 +17,8 @@
package com.android.launcher3.dragndrop;
import android.graphics.PointF;
+import android.os.SystemClock;
+import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
@@ -53,6 +55,31 @@
mVelocityTracker.addMovement(ev);
}
+ /**
+ * Same as {@link #recordMotionEvent}. It creates a temporary {@link MotionEvent} object
+ * using {@param event} for tracking velocity.
+ */
+ public void recordDragEvent(long dragStartTime, DragEvent event) {
+ final int motionAction;
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ motionAction = MotionEvent.ACTION_DOWN;
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ motionAction = MotionEvent.ACTION_MOVE;
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ motionAction = MotionEvent.ACTION_UP;
+ break;
+ default:
+ return;
+ }
+ MotionEvent emulatedEvent = MotionEvent.obtain(dragStartTime, SystemClock.uptimeMillis(),
+ motionAction, event.getX(), event.getY(), 0);
+ recordMotionEvent(emulatedEvent);
+ emulatedEvent.recycle();
+ }
+
public void releaseVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
new file mode 100644
index 0000000..1a99cc8
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 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.dragndrop;
+
+import android.content.ClipDescription;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingItemPreviewProvider;
+
+import java.util.UUID;
+
+/**
+ * {@link DragSource} for handling drop from from a different window. This object is initialized
+ * in the source window and is passed on to the Launcher activity as an Intent extra.
+ */
+public class PinItemDragListener implements Parcelable, View.OnDragListener, DragSource {
+
+ private static final String TAG = "PinItemDragListener";
+
+ private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
+ public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
+
+ private final PinItemRequestCompat mRequest;
+
+ // Position of preview relative to the touch location
+ private final Rect mPreviewRect;
+
+ // Randomly generated id used to verify the drag event.
+ private final String mId;
+
+ private Launcher mLauncher;
+ private DragController mDragController;
+ private long mDragStartTime;
+
+ public PinItemDragListener(PinItemRequestCompat request, Rect previewRect) {
+ mRequest = request;
+ mPreviewRect = previewRect;
+ mId = UUID.randomUUID().toString();
+ }
+
+ private PinItemDragListener(Parcel parcel) {
+ mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
+ mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
+ mId = parcel.readString();
+ }
+
+ public String getMimeType() {
+ return MIME_TYPE_PREFIX + mId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ mRequest.writeToParcel(parcel, i);
+ mPreviewRect.writeToParcel(parcel, i);
+ parcel.writeString(mId);
+ }
+
+ public void setLauncher(Launcher launcher) {
+ mLauncher = launcher;
+ mDragController = launcher.getDragController();
+ }
+
+ @Override
+ public boolean onDrag(View view, DragEvent event) {
+ if (mLauncher == null || mDragController == null) {
+ postCleanup();
+ return false;
+ }
+ if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+ if (onDragStart(event)) {
+ return true;
+ } else {
+ postCleanup();
+ return false;
+ }
+ }
+ return mDragController.onDragEvent(mDragStartTime, event);
+ }
+
+ private boolean onDragStart(DragEvent event) {
+ if (!mRequest.isValid()) {
+ return false;
+ }
+ ClipDescription desc = event.getClipDescription();
+ if (desc == null || !desc.hasMimeType(getMimeType())) {
+ Log.e(TAG, "Someone started a dragAndDrop before us.");
+ return false;
+ }
+
+ if (mLauncher.isWorkspaceLocked()) {
+ // TODO: implement wait
+ return false;
+ }
+
+ final PendingAddItemInfo item;
+ final Bitmap preview;
+
+ Point dragShift = new Point(mPreviewRect.left, mPreviewRect.top);
+ if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+ item = new PendingAddShortcutInfo(
+ new PinShortcutRequestActivityInfo(mRequest, mLauncher));
+
+ ShortcutInfoCompat compat = new ShortcutInfoCompat(mRequest.getShortcutInfo());
+ Bitmap icon = LauncherIcons.createShortcutIcon(compat, mLauncher, false /* badged */);
+
+ // Create a preview same as the workspace cell size and draw the icon at the
+ // appropriate position.
+ int[] size = mLauncher.getWorkspace().estimateItemSize(item, true, false);
+ preview = Bitmap.createBitmap(size[0], size[1], Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(preview);
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ c.drawBitmap(icon, (size[0] - icon.getWidth()) / 2,
+ (size[1] - icon.getHeight() - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2,
+ new Paint(Paint.FILTER_BITMAP_FLAG));
+ } else {
+ PendingAddWidgetInfo info = new PendingAddWidgetInfo(
+ LauncherAppWidgetProviderInfo.fromProviderInfo(
+ mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher)));
+ int[] size = mLauncher.getWorkspace().estimateItemSize(info, true, false);
+
+ float minScale = 1.25f;
+ int maxWidth = Math.min((int) (mPreviewRect.width() * minScale), size[0]);
+ int[] previewSizeBeforeScale = new int[1];
+ preview = LauncherAppState.getInstance(mLauncher).getWidgetCache()
+ .generateWidgetPreview(mLauncher, info.info, maxWidth, null,
+ previewSizeBeforeScale);
+
+ dragShift.offset(
+ (mPreviewRect.width() - preview.getWidth()) / 2,
+ (mPreviewRect.height() - preview.getHeight()) / 2);
+ item = info;
+ }
+
+ PendingItemPreviewProvider previewProvider =
+ new PendingItemPreviewProvider(new View(mLauncher), item, preview);
+
+ // Since we are not going through the workspace for starting the drag, set drag related
+ // information on the workspace before starting the drag.
+ mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
+
+ Point downPos = new Point((int) event.getX(), (int) event.getY());
+ DragOptions options = new DragOptions();
+ options.systemDndStartPoint = downPos;
+
+ int x = downPos.x + dragShift.x;
+ int y = downPos.y + dragShift.y;
+ mDragController.startDrag(
+ preview, x, y, this, item, null, null, 1f, options);
+ mDragStartTime = SystemClock.uptimeMillis();
+ return true;
+ }
+
+ @Override
+ public boolean supportsAppInfoDropTarget() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDeleteDropTarget() {
+ return false;
+ }
+
+ @Override
+ public float getIntrinsicIconScaleFactor() {
+ return 1f;
+ }
+
+ @Override
+ public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
+ boolean success) {
+ if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+ !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+ // Exit spring loaded mode if we have not successfully dropped or have not handled the
+ // drop in Workspace
+ mLauncher.exitSpringLoadedDragModeDelayed(true,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ }
+ postCleanup();
+ }
+
+ @Override
+ public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+ LauncherLogProto.Target targetParent) {
+ // TODO: We should probably log something
+ }
+
+ private void postCleanup() {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ removeListener();
+ }
+ });
+ }
+
+ public void removeListener() {
+ if (mLauncher != null) {
+ mLauncher.getDragLayer().setOnDragListener(null);
+ }
+ }
+
+ public static final Parcelable.Creator<PinItemDragListener> CREATOR =
+ new Parcelable.Creator<PinItemDragListener>() {
+ public PinItemDragListener createFromParcel(Parcel source) {
+ return new PinItemDragListener(source);
+ }
+
+ public PinItemDragListener[] newArray(int size) {
+ return new PinItemDragListener[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index d1f878a..2121b43 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -26,7 +26,9 @@
import android.os.Build;
import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.compat.PinItemRequestCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
/**
@@ -40,12 +42,15 @@
// actual existing class.
private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut";
+ private final PinItemRequestCompat mRequest;
private final ShortcutInfo mInfo;
private final Context mContext;
- public PinShortcutRequestActivityInfo(ShortcutInfo info, Context context) {
- super(new ComponentName(info.getPackage(), DUMMY_COMPONENT_CLASS), info.getUserHandle());
- mInfo = info;
+ public PinShortcutRequestActivityInfo(PinItemRequestCompat request, Context context) {
+ super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS),
+ request.getShortcutInfo().getUserHandle());
+ mRequest = request;
+ mInfo = request.getShortcutInfo();
mContext = context;
}
@@ -61,8 +66,9 @@
}
@Override
- public boolean startConfigActivity(Activity activity, int requestCode) {
- throw new RuntimeException("Not supported");
+ public boolean startConfigActivity(Launcher activity, int requestCode) {
+ activity.onActivityResult(requestCode, Activity.RESULT_OK, mRequest.toIntent());
+ return true;
}
@Override
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
new file mode 100644
index 0000000..4ed32b5
--- /dev/null
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 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.popup;
+
+import android.content.ComponentName;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.badging.NotificationListener;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageUserKey;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provides data for the popup menu that appears after long-clicking on apps.
+ */
+public class PopupDataProvider implements NotificationListener.NotificationsChangedListener {
+
+ private static final boolean LOGD = false;
+ private static final String TAG = "PopupDataProvider";
+
+ private final Launcher mLauncher;
+
+ /** Maps launcher activity components to their list of shortcut ids. */
+ private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
+ /** Maps packages to their BadgeInfo's . */
+ private Map<PackageUserKey, BadgeInfo> mPackageUserToBadgeInfos = new HashMap<>();
+
+ public PopupDataProvider(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ @Override
+ public void onNotificationPosted(PackageUserKey postedPackageUserKey, String notificationKey) {
+ BadgeInfo oldBadgeInfo = mPackageUserToBadgeInfos.get(postedPackageUserKey);
+ if (oldBadgeInfo == null) {
+ BadgeInfo newBadgeInfo = new BadgeInfo(postedPackageUserKey);
+ newBadgeInfo.addNotificationKey(notificationKey);
+ mPackageUserToBadgeInfos.put(postedPackageUserKey, newBadgeInfo);
+ mLauncher.updateIconBadges(Collections.singleton(postedPackageUserKey));
+ } else if (oldBadgeInfo.addNotificationKey(notificationKey)) {
+ mLauncher.updateIconBadges(Collections.singleton(postedPackageUserKey));
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(PackageUserKey removedPackageUserKey, String notificationKey) {
+ BadgeInfo oldBadgeInfo = mPackageUserToBadgeInfos.get(removedPackageUserKey);
+ if (oldBadgeInfo != null && oldBadgeInfo.removeNotificationKey(notificationKey)) {
+ if (oldBadgeInfo.getNotificationCount() == 0) {
+ mPackageUserToBadgeInfos.remove(removedPackageUserKey);
+ }
+ mLauncher.updateIconBadges(Collections.singleton(removedPackageUserKey));
+ }
+ }
+
+ @Override
+ public void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications) {
+ if (activeNotifications == null) return;
+ // This will contain the PackageUserKeys which have updated badges.
+ HashMap<PackageUserKey, BadgeInfo> updatedBadges = new HashMap<>(mPackageUserToBadgeInfos);
+ mPackageUserToBadgeInfos.clear();
+ for (StatusBarNotification notification : activeNotifications) {
+ PackageUserKey packageUserKey = PackageUserKey.fromNotification(notification);
+ BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(packageUserKey);
+ if (badgeInfo == null) {
+ badgeInfo = new BadgeInfo(packageUserKey);
+ mPackageUserToBadgeInfos.put(packageUserKey, badgeInfo);
+ }
+ badgeInfo.addNotificationKey(notification.getKey());
+ }
+
+ // Add and remove from updatedBadges so it contains the PackageUserKeys of updated badges.
+ for (PackageUserKey packageUserKey : mPackageUserToBadgeInfos.keySet()) {
+ BadgeInfo prevBadge = updatedBadges.get(packageUserKey);
+ BadgeInfo newBadge = mPackageUserToBadgeInfos.get(packageUserKey);
+ if (prevBadge == null) {
+ updatedBadges.put(packageUserKey, newBadge);
+ } else {
+ if (!prevBadge.shouldBeInvalidated(newBadge)) {
+ updatedBadges.remove(packageUserKey);
+ }
+ }
+ }
+
+ if (!updatedBadges.isEmpty()) {
+ mLauncher.updateIconBadges(updatedBadges.keySet());
+ }
+ }
+
+ public void setDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+ mDeepShortcutMap = deepShortcutMapCopy;
+ if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
+ }
+
+ public List<String> getShortcutIdsForItem(ItemInfo info) {
+ if (!DeepShortcutManager.supportsShortcuts(info)) {
+ return Collections.EMPTY_LIST;
+ }
+ ComponentName component = info.getTargetComponent();
+ if (component == null) {
+ return Collections.EMPTY_LIST;
+ }
+
+ List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
+ return ids == null ? Collections.EMPTY_LIST : ids;
+ }
+
+ public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
+ if (!DeepShortcutManager.supportsShortcuts(info)) {
+ return null;
+ }
+
+ return mPackageUserToBadgeInfos.get(PackageUserKey.fromItemInfo(info));
+ }
+
+ public String[] getNotificationKeysForItem(ItemInfo info) {
+ BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(PackageUserKey.fromItemInfo(info));
+ Set<String> notificationKeys = badgeInfo.getNotificationKeys();
+ return notificationKeys.toArray(new String[notificationKeys.size()]);
+ }
+
+ /** This makes a potentially expensive binder call and should be run on a background thread. */
+ public List<StatusBarNotification> getStatusBarNotificationsForKeys(String[] notificationKeys) {
+ NotificationListener notificationListener = NotificationListener.getInstance();
+ return notificationListener == null ? Collections.EMPTY_LIST
+ : notificationListener.getNotificationsForKeys(notificationKeys);
+ }
+
+ public void cancelNotification(String notificationKey) {
+ NotificationListener notificationListener = NotificationListener.getInstance();
+ if (notificationListener == null) {
+ return;
+ }
+ notificationListener.cancelNotification(notificationKey);
+ }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
index db2654c..5e12a57 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -718,7 +718,8 @@
icon.clearFocus();
return null;
}
- List<String> ids = launcher.getShortcutIdsForItem((ItemInfo) icon.getTag());
+ List<String> ids = launcher.getPopupDataProvider().getShortcutIdsForItem(
+ (ItemInfo) icon.getTag());
if (!ids.isEmpty()) {
final DeepShortcutsContainer container =
(DeepShortcutsContainer) launcher.getLayoutInflater().inflate(
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
new file mode 100644
index 0000000..d08b0e9
--- /dev/null
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -0,0 +1,51 @@
+package com.android.launcher3.util;
+
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import com.android.launcher3.ItemInfo;
+
+import java.util.Arrays;
+
+/** Creates a hash key based on package name and user. */
+public class PackageUserKey {
+
+ private String mPackageName;
+ private UserHandle mUser;
+ private int mHashCode;
+
+ public static PackageUserKey fromItemInfo(ItemInfo info) {
+ return new PackageUserKey(info.getTargetComponent().getPackageName(), info.user);
+ }
+
+ public static PackageUserKey fromNotification(StatusBarNotification notification) {
+ return new PackageUserKey(notification.getPackageName(), notification.getUser());
+ }
+
+ public PackageUserKey(String packageName, UserHandle user) {
+ update(packageName, user);
+ }
+
+ private void update(String packageName, UserHandle user) {
+ mPackageName = packageName;
+ mUser = user;
+ mHashCode = Arrays.hashCode(new Object[] {packageName, user});
+ }
+
+ /** This should only be called to avoid new object creations in a loop. */
+ public void updateFromItemInfo(ItemInfo info) {
+ update(info.getTargetComponent().getPackageName(), info.user);
+ }
+
+ @Override
+ public int hashCode() {
+ return mHashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PackageUserKey)) return false;
+ PackageUserKey otherKey = (PackageUserKey) obj;
+ return mPackageName.equals(otherKey.mPackageName) && mUser.equals(otherKey.mUser);
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 87247f4..455ec4e 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -144,12 +144,8 @@
}
}
- public int[] getPreviewSize() {
- int[] maxSize = new int[2];
-
- maxSize[0] = mPresetPreviewSize;
- maxSize[1] = mPresetPreviewSize;
- return maxSize;
+ public WidgetImageView getWidgetView() {
+ return mWidgetImage;
}
public void applyPreview(Bitmap bitmap) {
@@ -166,12 +162,8 @@
if (mActiveRequest != null) {
return;
}
- int[] size = getPreviewSize();
- if (DEBUG) {
- Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
- getTagToString(), size[0], size[1]));
- }
- mActiveRequest = mWidgetPreviewLoader.getPreview(mItem, size[0], size[1], this);
+ mActiveRequest = mWidgetPreviewLoader.getPreview(
+ mItem, mPresetPreviewSize, mPresetPreviewSize, this);
}
@Override
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 4cad836..ffb86e4 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -39,4 +39,6 @@
public static final boolean LIGHT_STATUS_BAR = false;
// When enabled allows to use any point on the fast scrollbar to start dragging.
public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
+ // When enabled icons are badged with the number of notifications associated with that app.
+ public static final boolean BADGE_ICONS = true;
}