summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/widget
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/widget')
-rw-r--r--src/com/android/launcher3/widget/PackageItemInfo.java57
-rw-r--r--src/com/android/launcher3/widget/PendingAddShortcutInfo.java44
-rw-r--r--src/com/android/launcher3/widget/PendingAddWidgetInfo.java91
-rw-r--r--src/com/android/launcher3/widget/WidgetCell.java338
-rw-r--r--src/com/android/launcher3/widget/WidgetImageView.java48
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java376
-rw-r--r--src/com/android/launcher3/widget/WidgetsListAdapter.java188
-rw-r--r--src/com/android/launcher3/widget/WidgetsModel.java136
-rw-r--r--src/com/android/launcher3/widget/WidgetsRowView.java90
-rw-r--r--src/com/android/launcher3/widget/WidgetsRowViewHolder.java36
10 files changed, 1404 insertions, 0 deletions
diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/widget/PackageItemInfo.java
new file mode 100644
index 000000000..d7edf2294
--- /dev/null
+++ b/src/com/android/launcher3/widget/PackageItemInfo.java
@@ -0,0 +1,57 @@
+/*
+ * 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.widget;
+
+import android.content.ComponentName;
+import android.graphics.Bitmap;
+
+import com.android.launcher3.ItemInfo;
+
+import java.util.Arrays;
+
+/**
+ * Represents a {@link Package} in the widget tray section.
+ */
+public class PackageItemInfo extends ItemInfo {
+ private static final String TAG = "PackageInfo";
+
+ /**
+ * A bitmap version of the application icon.
+ */
+ public Bitmap iconBitmap;
+
+ /**
+ * Indicates whether we're using a low res icon
+ */
+ public boolean usingLowResIcon;
+
+ public ComponentName componentName;
+
+ int flags = 0;
+
+ PackageItemInfo() {
+ }
+
+ @Override
+ public String toString() {
+ return "PackageItemInfo(title=" + title.toString() + " id=" + this.id
+ + " type=" + this.itemType + " container=" + this.container
+ + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
+ + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ + " user=" + user + ")";
+ }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
new file mode 100644
index 000000000..a56985083
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -0,0 +1,44 @@
+/*
+ * 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.widget;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PendingAddItemInfo;
+
+/**
+ * Meta data used for late binding of the short cuts.
+ *
+ * @see {@link PendingAddItemInfo}
+ */
+public class PendingAddShortcutInfo extends PendingAddItemInfo {
+
+ ActivityInfo activityInfo;
+
+ public PendingAddShortcutInfo(ActivityInfo activityInfo) {
+ this.activityInfo = activityInfo;
+ componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PendingAddShortcutInfo package=%s, name=%s",
+ activityInfo.packageName, activityInfo.name);
+ }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
new file mode 100644
index 000000000..db1699818
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -0,0 +1,91 @@
+/*
+ * 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.widget;
+
+import android.appwidget.AppWidgetHostView;
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PendingAddItemInfo;
+
+/**
+ * Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}.
+ *
+ * @see {@link PendingAddItemInfo}
+ */
+public class PendingAddWidgetInfo extends PendingAddItemInfo {
+ public int minWidth;
+ public int minHeight;
+ public int minResizeWidth;
+ public int minResizeHeight;
+ public int previewImage;
+ public int icon;
+ public LauncherAppWidgetProviderInfo info;
+ public AppWidgetHostView boundWidget;
+ public Bundle bindOptions = null;
+
+ public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, Parcelable data) {
+ if (i.isCustomWidget) {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+ } else {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+ }
+ this.info = i;
+ componentName = i.provider;
+ minWidth = i.minWidth;
+ minHeight = i.minHeight;
+ minResizeWidth = i.minResizeWidth;
+ minResizeHeight = i.minResizeHeight;
+ previewImage = i.previewImage;
+ icon = i.icon;
+
+ spanX = i.spanX;
+ spanY = i.spanY;
+ minSpanX = i.minSpanX;
+ minSpanY = i.minSpanY;
+ }
+
+ public boolean isCustomWidget() {
+ return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+ }
+
+ // Copy constructor
+ public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
+ minWidth = copy.minWidth;
+ minHeight = copy.minHeight;
+ minResizeWidth = copy.minResizeWidth;
+ minResizeHeight = copy.minResizeHeight;
+ previewImage = copy.previewImage;
+ icon = copy.icon;
+ info = copy.info;
+ boundWidget = copy.boundWidget;
+ componentName = copy.componentName;
+ itemType = copy.itemType;
+ spanX = copy.spanX;
+ spanY = copy.spanY;
+ minSpanX = copy.minSpanX;
+ minSpanY = copy.minSpanY;
+ bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PendingAddWidgetInfo package=%s, name=%s",
+ componentName.getPackageName(), componentName.getShortClassName());
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
new file mode 100644
index 000000000..ccd67ce41
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -0,0 +1,338 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
+/**
+ * The linear layout used strictly for the widget tray.
+ */
+public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
+
+ private static final String TAG = "PagedViewWidget";
+ private static final boolean DEBUG = false;
+
+ // Temporary preset width and height of the image to keep them aligned.
+ //private static final int PRESET_PREVIEW_HEIGHT = 480;
+ //private static final int PRESET_PREVIEW_WIDTH = 480;
+
+ private int mPresetPreviewSize;
+
+ private static WidgetCell sShortpressTarget = null;
+
+ private final Rect mOriginalImagePadding = new Rect();
+
+ private String mDimensionsFormatString;
+ private CheckForShortPress mPendingCheckForShortPress = null;
+ private ShortPressListener mShortPressListener = null;
+ private boolean mShortPressTriggered = false;
+ private boolean mIsAppWidget;
+ private Object mInfo;
+
+ private WidgetPreviewLoader mWidgetPreviewLoader;
+ private PreviewLoadRequest mActiveRequest;
+
+ public WidgetCell(Context context) {
+ this(context, null);
+ }
+
+ public WidgetCell(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WidgetCell(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final Resources r = context.getResources();
+ mDimensionsFormatString = r.getString(R.string.widget_dims_format);
+ mPresetPreviewSize = r.getDimensionPixelSize(R.dimen.widget_preview_size);
+
+ setWillNotDraw(false);
+ setClipToPadding(false);
+ setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final ImageView image = (ImageView) findViewById(R.id.widget_preview);
+ mOriginalImagePadding.left = image.getPaddingLeft();
+ mOriginalImagePadding.top = image.getPaddingTop();
+ mOriginalImagePadding.right = image.getPaddingRight();
+ mOriginalImagePadding.bottom = image.getPaddingBottom();
+
+ // Ensure we are using the right text size
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ TextView name = (TextView) findViewById(R.id.widget_name);
+ if (name != null) {
+ name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+ }
+ TextView dims = (TextView) findViewById(R.id.widget_dims);
+ if (dims != null) {
+ dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (DEBUG) {
+ Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
+ }
+ super.onDetachedFromWindow();
+ deletePreview(false);
+ }
+
+ public void deletePreview(boolean recycleImage) {
+ if (recycleImage) {
+ final ImageView image = (ImageView) findViewById(R.id.widget_preview);
+ if (image != null) {
+ image.setImageDrawable(null);
+ }
+ }
+
+ if (mActiveRequest != null) {
+ mActiveRequest.cancel(recycleImage);
+ mActiveRequest = null;
+ }
+ }
+
+ public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
+ int maxWidth, WidgetPreviewLoader loader) {
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+ mIsAppWidget = true;
+ mInfo = info;
+ final ImageView image = (ImageView) findViewById(R.id.widget_preview);
+ if (maxWidth > -1) {
+ image.setMaxWidth(maxWidth);
+ }
+ final TextView name = (TextView) findViewById(R.id.widget_name);
+ name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
+ final TextView dims = (TextView) findViewById(R.id.widget_dims);
+ if (dims != null) {
+ int hSpan = Math.min(info.spanX, (int) grid.numColumns);
+ int vSpan = Math.min(info.spanY, (int) grid.numRows);
+ dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
+ }
+ mWidgetPreviewLoader = loader;
+ }
+
+ public void applyFromResolveInfo(
+ PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
+ mIsAppWidget = false;
+ mInfo = info;
+ CharSequence label = info.loadLabel(pm);
+ final TextView name = (TextView) findViewById(R.id.widget_name);
+ name.setText(label);
+ final TextView dims = (TextView) findViewById(R.id.widget_dims);
+ if (dims != null) {
+ dims.setText(String.format(mDimensionsFormatString, 1, 1));
+ }
+ mWidgetPreviewLoader = loader;
+ }
+
+ public int[] getPreviewSize() {
+ final ImageView i = (ImageView) findViewById(R.id.widget_preview);
+ int[] maxSize = new int[2];
+ maxSize[0] = mPresetPreviewSize;
+ maxSize[1] = mPresetPreviewSize;
+ return maxSize;
+ }
+
+ public void applyPreview(Bitmap bitmap) {
+ FastBitmapDrawable preview = new FastBitmapDrawable(bitmap);
+ final WidgetImageView image =
+ (WidgetImageView) findViewById(R.id.widget_preview);
+ if (DEBUG) {
+ Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s",
+ getTagToString(), preview));
+ }
+ if (preview != null) {
+ image.mAllowRequestLayout = false;
+ image.setImageDrawable(preview);
+ if (mIsAppWidget) {
+ // center horizontally
+ int[] imageSize = getPreviewSize();
+ int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2;
+ image.setPadding(mOriginalImagePadding.left + centerAmount,
+ mOriginalImagePadding.top,
+ mOriginalImagePadding.right,
+ mOriginalImagePadding.bottom);
+ }
+ image.setAlpha(1f);
+ image.mAllowRequestLayout = true;
+ image.requestLayout();
+ }
+ }
+
+ void setShortPressListener(ShortPressListener listener) {
+ mShortPressListener = listener;
+ }
+
+ interface ShortPressListener {
+ void onShortPress(View v);
+ void cleanUpShortPress(View v);
+ }
+
+ class CheckForShortPress implements Runnable {
+ public void run() {
+ if (sShortpressTarget != null) return;
+ if (mShortPressListener != null) {
+ mShortPressListener.onShortPress(WidgetCell.this);
+ sShortpressTarget = WidgetCell.this;
+ }
+ mShortPressTriggered = true;
+ }
+ }
+
+ private void checkForShortPress() {
+ if (sShortpressTarget != null) return;
+ if (mPendingCheckForShortPress == null) {
+ mPendingCheckForShortPress = new CheckForShortPress();
+ }
+ postDelayed(mPendingCheckForShortPress, 120);
+ }
+
+ /**
+ * Remove the longpress detection timer.
+ */
+ private void removeShortPressCallback() {
+ if (mPendingCheckForShortPress != null) {
+ removeCallbacks(mPendingCheckForShortPress);
+ }
+ }
+
+ private void cleanUpShortPress() {
+ removeShortPressCallback();
+ if (mShortPressTriggered) {
+ if (mShortPressListener != null) {
+ mShortPressListener.cleanUpShortPress(WidgetCell.this);
+ }
+ mShortPressTriggered = false;
+ }
+ }
+
+ static void resetShortPressTarget() {
+ sShortpressTarget = null;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ cleanUpShortPress();
+ break;
+ case MotionEvent.ACTION_DOWN:
+ checkForShortPress();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ cleanUpShortPress();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ break;
+ }
+
+ // We eat up the touch events here, since the PagedView (which uses the same swiping
+ // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
+ // the user is scrolling between pages. This means that if the pages themselves don't
+ // handle touch events, it gets forwarded up to PagedView itself, and it's own
+ // onTouchEvent() handling will prevent further intercept touch events from being called
+ // (it's the same view in that case). This is not ideal, but to prevent more changes,
+ // we just always mark the touch event as handled.
+ return true;
+ }
+
+ public void ensurePreview() {
+ if (mActiveRequest != null) {
+ return;
+ }
+ int[] size = getPreviewSize();
+ if (DEBUG) {
+ Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
+ getTagToString(), size[0], size[1]));
+ }
+
+ if (size[0] <= 0 || size[1] <= 0) {
+ addOnLayoutChangeListener(this);
+ return;
+ }
+ Bitmap[] immediateResult = new Bitmap[1];
+ mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this,
+ immediateResult);
+ if (immediateResult[0] != null) {
+ applyPreview(immediateResult[0]);
+ }
+ }
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ removeOnLayoutChangeListener(this);
+ ensurePreview();
+ }
+
+ public int getActualItemWidth() {
+ ItemInfo info = (ItemInfo) getTag();
+ int[] size = getPreviewSize();
+ int cellWidth = LauncherAppState.getInstance()
+ .getDynamicGrid().getDeviceProfile().cellWidthPx;
+
+ return Math.min(size[0], info.spanX * cellWidth);
+ }
+
+ /**
+ * Helper method to get the string info of the tag.
+ */
+ private String getTagToString() {
+ if (getTag() instanceof PendingAddWidgetInfo) {
+ return ((PendingAddWidgetInfo)getTag()).toString();
+ } else if (getTag() instanceof PendingAddShortcutInfo) {
+ return ((PendingAddShortcutInfo)getTag()).toString();
+ }
+ return "";
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
new file mode 100644
index 000000000..75167bc7d
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -0,0 +1,48 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class WidgetImageView extends ImageView {
+ public boolean mAllowRequestLayout = true;
+
+ public WidgetImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void requestLayout() {
+ if (mAllowRequestLayout) {
+ super.requestLayout();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.clipRect(getScrollX() + getPaddingLeft(),
+ getScrollY() + getPaddingTop(),
+ getScrollX() + getRight() - getLeft() - getPaddingRight(),
+ getScrollY() + getBottom() - getTop() - getPaddingBottom());
+
+ super.onDraw(canvas);
+ canvas.restore();
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
new file mode 100644
index 000000000..6580ab4ff
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -0,0 +1,376 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DragController;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.Folder;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.Workspace;
+
+import java.util.ArrayList;
+
+/**
+ * The widgets list view container.
+ */
+public class WidgetsContainerView extends FrameLayout implements Insettable, View.OnTouchListener,
+ View.OnLongClickListener, DragSource{
+
+ private static final String TAG = "WidgetContainerView";
+ private static final boolean DEBUG = false;
+
+ /* {@link RecyclerView} will keep following # of views in cache, before recycling. */
+ private static final int WIDGET_CACHE_SIZE = 2;
+
+ /* Global instances that are used inside this container. */
+ private Launcher mLauncher;
+ private DragController mDragController;
+ private IconCache mIconCache;
+
+ /* Data model for the widget */
+ private WidgetsModel mWidgets;
+
+ /* Recycler view related member variables */
+ private RecyclerView mView;
+ private WidgetsListAdapter mAdapter;
+
+ /* Dragging related. */
+ private boolean mDraggingWidget = false; // TODO(hyunyoungs): seems not needed? check!
+ private Point mLastTouchDownPos = new Point();
+
+ /* Rendering related. */
+ private WidgetPreviewLoader mWidgetPreviewLoader;
+ private Rect mPadding = new Rect();
+
+ public WidgetsContainerView(Context context) {
+ this(context, null);
+ }
+
+ public WidgetsContainerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = (Launcher) context;
+ mDragController = mLauncher.getDragController();
+
+ mAdapter = new WidgetsListAdapter(context, this, mLauncher, this, mLauncher);
+ mWidgets = new WidgetsModel(context, mAdapter);
+ mAdapter.setWidgetsModel(mWidgets);
+ mIconCache = (LauncherAppState.getInstance()).getIconCache();
+
+ if (DEBUG) {
+ Log.d(TAG, "WidgetsContainerView constructor");
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ if (DEBUG) {
+ Log.d(TAG, String.format("onFinishInflate [widgets size=%d]",
+ mWidgets.getPackageSize()));
+ }
+ mView = (RecyclerView) findViewById(R.id.widgets_list_view);
+ mView.setAdapter(mAdapter);
+ mView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mView.setItemViewCacheSize(WIDGET_CACHE_SIZE);
+
+ mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+ getPaddingBottom());
+ }
+
+ //
+ // Returns views used for launcher transitions.
+ //
+
+ public View getContentView() {
+ return findViewById(R.id.widgets_list_view);
+ }
+
+ public View getRevealView() {
+ // TODO(hyunyoungs): temporarily use apps view transition.
+ return findViewById(R.id.widgets_reveal_view);
+ }
+
+ public void scrollToTop() {
+ mView.scrollToPosition(0);
+ if (DEBUG) {
+ Log.d(TAG, String.format("scrollToTop, [widgets size=%d]",
+ mWidgets.getPackageSize()));
+ }
+ }
+
+ //
+ // Touch related handling.
+ //
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("onLonglick [v=%s]", v));
+ }
+
+ // Return early if this is not initiated from a touch
+ if (!v.isInTouchMode()) return false;
+ // When we have exited all apps or are in transition, disregard long clicks
+ if (!mLauncher.isWidgetsViewVisible() ||
+ mLauncher.getWorkspace().isSwitchingState()) return false;
+ // Return if global dragging is not enabled
+ Log.d(TAG, String.format("onLonglick dragging enabled?.", v));
+ if (!mLauncher.isDraggingEnabled()) return false;
+
+ return beginDragging(v);
+ }
+
+ private boolean beginDragging(View v) {
+ if (v instanceof WidgetCell) {
+ if (!beginDraggingWidget((WidgetCell) v)) {
+ return false;
+ }
+ } else {
+ Log.e(TAG, "Unexpected dragging view: " + v);
+ }
+
+ // We delay entering spring-loaded mode slightly to make sure the UI
+ // thready is free of any work.
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // We don't enter spring-loaded mode if the drag has been cancelled
+ if (mLauncher.getDragController().isDragging()) {
+ // Go into spring loaded mode (must happen before we startDrag())
+ mLauncher.enterSpringLoadedDragMode();
+ }
+ }
+ }, 150);
+
+ return true;
+ }
+
+ private boolean beginDraggingWidget(WidgetCell v) {
+ mDraggingWidget = true;
+ // Get the widget preview as the drag representation
+ ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
+ PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
+
+ // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
+ // we abort the drag.
+ if (image.getDrawable() == null) {
+ mDraggingWidget = false;
+ return false;
+ }
+
+ // Compose the drag image
+ Bitmap preview;
+ Bitmap outline;
+ float scale = 1f;
+ Point previewPadding = null;
+
+ if (createItemInfo instanceof PendingAddWidgetInfo) {
+ // This can happen in some weird cases involving multi-touch. We can't start dragging
+ // the widget if this is null, so we break out.
+
+ PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
+ int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true);
+
+ FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
+ float minScale = 1.25f;
+ int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
+
+ int[] previewSizeBeforeScale = new int[1];
+ preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
+ maxWidth, null, previewSizeBeforeScale);
+ // Compare the size of the drag preview to the preview in the AppsCustomize tray
+ int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
+ v.getActualItemWidth());
+ scale = previewWidthInAppsCustomize / (float) preview.getWidth();
+
+ // The bitmap in the AppsCustomize tray is always the the same size, so there
+ // might be extra pixels around the preview itself - this accounts for that
+ if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
+ int padding =
+ (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
+ previewPadding = new Point(padding, 0);
+ }
+ } else {
+ PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
+ Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo);
+ preview = Utilities.createIconBitmap(icon, mLauncher);
+ createItemInfo.spanX = createItemInfo.spanY = 1;
+ }
+
+ // Don't clip alpha values for the drag outline if we're using the default widget preview
+ boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
+ (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
+
+ // Save the preview for the outline generation, then dim the preview
+ outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
+ false);
+
+ // Start the drag
+ mLauncher.lockScreenOrientation();
+ mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
+ mDragController.startDrag(image, preview, this, createItemInfo,
+ DragController.DRAG_ACTION_COPY, previewPadding, scale);
+ outline.recycle();
+ preview.recycle();
+ return true;
+ }
+
+ /*
+ * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent)
+ */
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ Log.d(TAG, String.format("onTouch [MotionEvent=%s]", ev));
+ if (ev.getAction() == MotionEvent.ACTION_DOWN ||
+ ev.getAction() == MotionEvent.ACTION_MOVE) {
+ mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY());
+ }
+ return false;
+ }
+
+ //
+ // Drag related handling methods that implement {@link DragSource} interface.
+ //
+
+ @Override
+ public boolean supportsFlingToDelete() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsAppInfoDropTarget() {
+ return true;
+ }
+
+ /*
+ * Both this method and {@link #supportsFlingToDelete} has to return {@code false} for the
+ * {@link DeleteDropTarget} to be invisible.)
+ */
+ @Override
+ public boolean supportsDeleteDropTarget() {
+ return false;
+ }
+
+ @Override
+ public float getIntrinsicIconScaleFactor() {
+ return 0;
+ }
+
+ @Override
+ public void onFlingToDeleteCompleted() {
+ // We just dismiss the drag when we fling, so cleanup here
+ mLauncher.exitSpringLoadedDragModeDelayed(true,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ mLauncher.unlockScreenOrientation(false);
+ }
+
+ @Override
+ public void onDropCompleted(View target, 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);
+ }
+ mLauncher.unlockScreenOrientation(false);
+
+ // Display an error message if the drag failed due to there not being enough space on the
+ // target layout we were dropping on.
+ if (!success) {
+ boolean showOutOfSpaceMessage = false;
+ if (target instanceof Workspace) {
+ int currentScreen = mLauncher.getCurrentWorkspaceScreen();
+ Workspace workspace = (Workspace) target;
+ CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
+ ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+ if (layout != null) {
+ layout.calculateSpans(itemInfo);
+ showOutOfSpaceMessage =
+ !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
+ }
+ }
+ if (showOutOfSpaceMessage) {
+ mLauncher.showOutOfSpaceMessage(false);
+ }
+ d.deferDragViewCleanupPostAnimation = false;
+ }
+ }
+
+ //
+ // Container rendering related.
+ //
+
+ /*
+ * @see Insettable#setInsets(Rect)
+ */
+ @Override
+ public void setInsets(Rect insets) {
+ setPadding(mPadding.left + insets.left, mPadding.top + insets.top,
+ mPadding.right + insets.right, mPadding.bottom + insets.bottom);
+ }
+
+ /**
+ * Initialize the widget data model.
+ */
+ public void addWidgets(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
+ mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm);
+ }
+
+ private WidgetPreviewLoader getWidgetPreviewLoader() {
+ if (mWidgetPreviewLoader == null) {
+ mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+ }
+ return mWidgetPreviewLoader;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
new file mode 100644
index 000000000..d0d1e60b4
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.List;
+
+/**
+ * List view adapter for the widget tray.
+ *
+ * <p>Memory vs. Performance:
+ * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
+ * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
+ * only a single type of view.
+ */
+public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
+
+ private static final String TAG = "WidgetsListAdapter";
+ private static final boolean DEBUG = false;
+
+ private Context mContext;
+ private Launcher mLauncher;
+ private LayoutInflater mLayoutInflater;
+ private IconCache mIconCache;
+
+ private WidgetsModel mWidgetsModel;
+ private WidgetPreviewLoader mWidgetPreviewLoader;
+
+ private View.OnTouchListener mTouchListener;
+ private View.OnClickListener mIconClickListener;
+ private View.OnLongClickListener mIconLongClickListener;
+
+
+ public WidgetsListAdapter(Context context,
+ View.OnTouchListener touchListener,
+ View.OnClickListener iconClickListener,
+ View.OnLongClickListener iconLongClickListener,
+ Launcher launcher) {
+ mLayoutInflater = LayoutInflater.from(context);
+ mContext = context;
+
+ mTouchListener = touchListener;
+ mIconClickListener = iconClickListener;
+ mIconLongClickListener = iconLongClickListener;
+
+ mLauncher = launcher;
+ mIconCache = LauncherAppState.getInstance().getIconCache();
+ }
+
+ public void setWidgetsModel(WidgetsModel w) {
+ mWidgetsModel = w;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mWidgetsModel.getPackageSize();
+ }
+
+ @Override
+ public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
+ String packageName = mWidgetsModel.getPackageName(pos);
+ List<Object> infoList = mWidgetsModel.getSortedWidgets(packageName);
+
+ ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "onBindViewHolder [pos=%d, packageName=%s, widget#=%d, row.getChildCount=%d]",
+ pos, packageName, infoList.size(), row.getChildCount()));
+ }
+
+ // Add more views.
+ // if there are too many, hide them.
+ int diff = infoList.size() - row.getChildCount();
+ if (diff > 0) {
+ for (int i = 0; i < diff; i++) {
+ WidgetCell widget = new WidgetCell(mContext);
+ widget = (WidgetCell) mLayoutInflater.inflate(
+ R.layout.widget_cell, row, false);
+
+ // set up touch.
+ widget.setOnClickListener(mIconClickListener);
+ widget.setOnLongClickListener(mIconLongClickListener);
+ widget.setOnTouchListener(mTouchListener);
+ row.addView(widget);
+ }
+ } else if (diff < 0) {
+ for (int i=infoList.size() ; i < row.getChildCount(); i++) {
+ row.getChildAt(i).setVisibility(View.GONE);
+ }
+ }
+
+ // Bind the views in the application info section.
+ PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(packageName);
+ if (infoOut.usingLowResIcon) {
+ mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
+ false /* useLowResIcon */, infoOut);
+ }
+ ((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title);
+ ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image);
+ iv.setImageBitmap(infoOut.iconBitmap);
+
+ // Bind the view in the widget horizontal tray region.
+ for (int i=0; i < infoList.size(); i++) {
+ WidgetCell widget = (WidgetCell) row.getChildAt(i);
+ if (getWidgetPreviewLoader() == null || widget == null) {
+ return;
+ }
+ if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
+ LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
+ PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null);
+ widget.setTag(pawi);
+ widget.applyFromAppWidgetProviderInfo(info, -1, mWidgetPreviewLoader);
+ } else if (infoList.get(i) instanceof ResolveInfo) {
+ ResolveInfo info = (ResolveInfo) infoList.get(i);
+ PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo);
+ widget.setTag(pasi);
+ widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader);
+ }
+ widget.setVisibility(View.VISIBLE);
+ widget.ensurePreview();
+ }
+ // TODO(hyunyoungs): Draw the scrollable indicator.
+ }
+
+ @Override
+ public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("\nonCreateViewHolder, [widget#=%d]", viewType));
+ }
+
+ ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
+ R.layout.widgets_list_row_view, parent, false);
+ return new WidgetsRowViewHolder(container);
+ }
+
+ @Override
+ public long getItemId(int pos) {
+ return pos;
+ }
+
+ private WidgetPreviewLoader getWidgetPreviewLoader() {
+ if (mWidgetPreviewLoader == null) {
+ mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+ }
+ return mWidgetPreviewLoader;
+ }
+
+ /**
+ * TODO(hyunyoungs): this is temporary. Figure out the width of each widget cell
+ * and then check if the total sum is longer than the parent width.
+ */
+ private void addScrollableIndicator(int contentSize, ViewGroup parent) {
+ if (contentSize > 2) {
+ ViewGroup indicator = (ViewGroup) parent.findViewById(R.id.scrollable_indicator);
+ indicator.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java
new file mode 100644
index 000000000..c400d6366
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsModel.java
@@ -0,0 +1,136 @@
+
+package com.android.launcher3.widget;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Widgets data model that is used by the adapters of the widget views and controllers.
+ *
+ * <p> The widgets and shortcuts are organized using package name as its index.
+ */
+public class WidgetsModel {
+
+ private static final String TAG = "WidgetsModel";
+ private static final boolean DEBUG = false;
+
+ /* List of packages that is tracked by this model. */
+ private List<String> mPackageNames = new ArrayList<>();
+
+ private Map<String, PackageItemInfo> mPackageItemInfoList = new HashMap<>();
+
+ /* Map of widgets and shortcuts that are tracked per package. */
+ private Map<String, ArrayList<Object>> mWidgetsList = new HashMap<>();
+
+ /* Notifies the adapter when data changes. */
+ private RecyclerView.Adapter mAdapter;
+
+ private Comparator mWidgetAndShortcutNameComparator;
+
+ private IconCache mIconCache;
+
+ public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
+ mAdapter = adapter;
+ mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context);
+ mIconCache = LauncherAppState.getInstance().getIconCache();
+ }
+
+ // Access methods that may be deleted if the private fields are made package-private.
+ public int getPackageSize() {
+ return mPackageNames.size();
+ }
+
+ // Access methods that may be deleted if the private fields are made package-private.
+ public String getPackageName(int pos) {
+ return mPackageNames.get(pos);
+ }
+
+ public PackageItemInfo getPackageItemInfo(String packageName) {
+ return mPackageItemInfoList.get(packageName);
+ }
+
+ public List<Object> getSortedWidgets(String packageName) {
+ return mWidgetsList.get(packageName);
+ }
+
+ public void addWidgetsAndShortcuts(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
+ if (DEBUG) {
+ Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size());
+ }
+
+ // clear the lists.
+ mPackageNames.clear();
+ mWidgetsList.clear();
+
+ // add and update.
+ for (Object o: widgetsShortcuts) {
+ String packageName = "";
+ if (o instanceof LauncherAppWidgetProviderInfo) {
+ LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
+ packageName = widgetInfo.provider.getPackageName();
+ } else if (o instanceof ResolveInfo) {
+ ResolveInfo resolveInfo = (ResolveInfo) o;
+ packageName = resolveInfo.activityInfo.packageName;
+ } else {
+ Log.e(TAG, String.format("addWidgetsAndShortcuts, nothing added for class=%s",
+ o.getClass().toString()));
+
+ }
+
+ ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(packageName);
+ if (widgetsShortcutsList != null) {
+ widgetsShortcutsList.add(o);
+ } else {
+ widgetsShortcutsList = new ArrayList<Object>();
+ widgetsShortcutsList.add(o);
+ mWidgetsList.put(packageName, widgetsShortcutsList);
+ mPackageNames.add(packageName);
+ }
+ }
+ for (String packageName: mPackageNames) {
+ PackageItemInfo pInfo = mPackageItemInfoList.get(packageName);
+ if (pInfo == null) {
+ pInfo = new PackageItemInfo();
+ mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
+ true /* useLowResIcon */, pInfo);
+ mPackageItemInfoList.put(packageName, pInfo);
+ }
+ }
+
+ // sort.
+ sortPackageList();
+ for (String packageName: mPackageNames) {
+ Collections.sort(mWidgetsList.get(packageName), mWidgetAndShortcutNameComparator);
+ }
+
+ // notify.
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private void sortPackageList() {
+ Collections.sort(mPackageNames, new Comparator<String>() {
+ @Override
+ public int compare(String lhs, String rhs) {
+ String lhsTitle = mPackageItemInfoList.get(lhs).title.toString();
+ String rhsTitle = mPackageItemInfoList.get(rhs).title.toString();
+ return lhsTitle.compareTo(rhsTitle);
+ }
+ });
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRowView.java b/src/com/android/launcher3/widget/WidgetsRowView.java
new file mode 100644
index 000000000..54667384b
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsRowView.java
@@ -0,0 +1,90 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+/**
+ * Layout used for widget tray rows for each app. For performance, this view can be replaced with
+ * a {@link RecyclerView} in the future if we settle on scrollable single row for the widgets.
+ * If we decide on collapsable grid, then HorizontalScrollView can be replaced with a
+ * {@link GridLayout}.
+ */
+public class WidgetsRowView extends HorizontalScrollView {
+ static final String TAG = "WidgetsRow";
+
+ private Runnable mOnLayoutListener;
+ private String mAppName;
+
+ public WidgetsRowView(Context context, String appName) {
+ super(context, null, 0);
+ mAppName = appName;
+ }
+
+ /**
+ * Clears all the key listeners for the individual widgets.
+ */
+ public void resetChildrenOnKeyListeners() {
+ int childCount = getChildCount();
+ for (int j = 0; j < childCount; ++j) {
+ getChildAt(j).setOnKeyListener(null);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ TextView tv = (TextView) findViewById(R.id.widget_name);
+ tv.setText(mAppName);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mOnLayoutListener = null;
+ }
+
+ public void setOnLayoutListener(Runnable r) {
+ mOnLayoutListener = r;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mOnLayoutListener != null) {
+ mOnLayoutListener.run();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean result = super.onTouchEvent(event);
+ return result;
+ }
+
+ public static class LayoutParams extends FrameLayout.LayoutParams {
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
new file mode 100644
index 000000000..99a192c89
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -0,0 +1,36 @@
+/*
+ * 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.widget;
+
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class WidgetsRowViewHolder extends ViewHolder {
+
+ ViewGroup mContent;
+
+ public WidgetsRowViewHolder(ViewGroup v) {
+ super(v);
+ mContent = v;
+ }
+
+ ViewGroup getContent() {
+ return mContent;
+ }
+}