summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/widget/WidgetsContainerView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/widget/WidgetsContainerView.java')
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java376
1 files changed, 376 insertions, 0 deletions
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