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.java370
1 files changed, 370 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..5afd7c493
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -0,0 +1,370 @@
+/*
+ * 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.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView.State;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragController;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Folder;
+import com.android.launcher3.IconCache;
+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 com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.util.Thunk;
+
+/**
+ * The widgets list view container.
+ */
+public class WidgetsContainerView extends BaseContainerView
+ implements View.OnLongClickListener, View.OnClickListener, DragSource{
+
+ private static final String TAG = "WidgetsContainerView";
+ private static final boolean DEBUG = false;
+
+ /* Coefficient multiplied to the screen height for preloading widgets. */
+ private static final int PRELOAD_SCREEN_HEIGHT_MULTIPLE = 1;
+
+ /* Global instances that are used inside this container. */
+ @Thunk Launcher mLauncher;
+ private DragController mDragController;
+ private IconCache mIconCache;
+
+ /* Recycler view related member variables */
+ private View mContent;
+ private WidgetsRecyclerView mView;
+ private WidgetsListAdapter mAdapter;
+
+ /* Touch handling related member variables. */
+ private Toast mWidgetInstructionToast;
+
+ /* 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) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = (Launcher) context;
+ mDragController = mLauncher.getDragController();
+ mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
+ mIconCache = (LauncherAppState.getInstance()).getIconCache();
+ if (DEBUG) {
+ Log.d(TAG, "WidgetsContainerView constructor");
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mContent = findViewById(R.id.content);
+ mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view);
+ mView.setAdapter(mAdapter);
+
+ // This extends the layout space so that preloading happen for the {@link RecyclerView}
+ mView.setLayoutManager(new LinearLayoutManager(getContext()) {
+ @Override
+ protected int getExtraLayoutSpace(State state) {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ return super.getExtraLayoutSpace(state)
+ + grid.availableHeightPx * PRELOAD_SCREEN_HEIGHT_MULTIPLE;
+ }
+ });
+ mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+ getPaddingBottom());
+ }
+
+ //
+ // Returns views used for launcher transitions.
+ //
+
+ public View getContentView() {
+ return mView;
+ }
+
+ public View getRevealView() {
+ // TODO(hyunyoungs): temporarily use apps view transition.
+ return findViewById(R.id.widgets_reveal_view);
+ }
+
+ public void scrollToTop() {
+ mView.scrollToPosition(0);
+ }
+
+ //
+ // Touch related handling.
+ //
+
+ @Override
+ public void onClick(View v) {
+ // When we have exited widget tray or are in transition, disregard clicks
+ if (!mLauncher.isWidgetsViewVisible()
+ || mLauncher.getWorkspace().isSwitchingState()
+ || !(v instanceof WidgetCell)) return;
+
+ // Let the user know that they have to long press to add a widget
+ if (mWidgetInstructionToast != null) {
+ mWidgetInstructionToast.cancel();
+ }
+ mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
+ Toast.LENGTH_SHORT);
+ mWidgetInstructionToast.show();
+ }
+
+ @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;
+
+ boolean status = beginDragging(v);
+ if (status && v.getTag() instanceof PendingAddWidgetInfo) {
+ WidgetHostViewLoader hostLoader = new WidgetHostViewLoader(mLauncher, v);
+ boolean preloadStatus = hostLoader.preloadWidget();
+ if (DEBUG) {
+ Log.d(TAG, String.format("preloading widget [status=%s]", preloadStatus));
+ }
+ mLauncher.getDragController().addDragListener(hostLoader);
+ }
+ return status;
+ }
+
+ private boolean beginDragging(View v) {
+ if (v instanceof WidgetCell) {
+ if (!beginDraggingWidget((WidgetCell) v)) {
+ return false;
+ }
+ } else {
+ Log.e(TAG, "Unexpected dragging view: " + v);
+ }
+
+ // 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();
+ }
+
+ return true;
+ }
+
+ private boolean beginDraggingWidget(WidgetCell v) {
+ // Get the widget preview as the drag representation
+ WidgetImageView image = (WidgetImageView) 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.getBitmap() == null) {
+ return false;
+ }
+
+ // Compose the drag image
+ Bitmap preview;
+ float scale = 1f;
+ final Rect bounds = image.getBitmapBounds();
+
+ 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);
+
+ Bitmap icon = image.getBitmap();
+ float minScale = 1.25f;
+ int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]);
+
+ int[] previewSizeBeforeScale = new int[1];
+ preview = getWidgetPreviewLoader().generateWidgetPreview(mLauncher,
+ createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale);
+
+ if (previewSizeBeforeScale[0] < icon.getWidth()) {
+ // The icon has extra padding around it.
+ int padding = (icon.getWidth() - previewSizeBeforeScale[0]) / 2;
+ if (icon.getWidth() > image.getWidth()) {
+ padding = padding * image.getWidth() / icon.getWidth();
+ }
+
+ bounds.left += padding;
+ bounds.right -= padding;
+ }
+ scale = bounds.width() / (float) preview.getWidth();
+ } else {
+ PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
+ Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo);
+ preview = Utilities.createIconBitmap(icon, mLauncher);
+ createItemInfo.spanX = createItemInfo.spanY = 1;
+ scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth();
+ }
+
+ // 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));
+
+ // Start the drag
+ mLauncher.lockScreenOrientation();
+ mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
+ mDragController.startDrag(image, preview, this, createItemInfo,
+ bounds, DragController.DRAG_ACTION_COPY, scale);
+
+ preview.recycle();
+ return true;
+ }
+
+ //
+ // 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.
+ //
+
+ @Override
+ protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
+ // Apply the top-bottom padding to the content itself so that the launcher transition is
+ // clipped correctly
+ mContent.setPadding(0, padding.top, 0, padding.bottom);
+
+ // TODO: Use quantum_panel_dark instead of quantum_panel_shape_dark.
+ InsetDrawable background = new InsetDrawable(
+ getResources().getDrawable(R.drawable.quantum_panel_shape_dark), padding.left, 0,
+ padding.right, 0);
+ Rect bgPadding = new Rect();
+ background.getPadding(bgPadding);
+ mView.setBackground(background);
+ getRevealView().setBackground(background.getConstantState().newDrawable());
+ mView.updateBackgroundPadding(bgPadding);
+ }
+
+ /**
+ * Initialize the widget data model.
+ */
+ public void addWidgets(WidgetsModel model) {
+ mView.setWidgets(model);
+ mAdapter.setWidgetsModel(model);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private WidgetPreviewLoader getWidgetPreviewLoader() {
+ if (mWidgetPreviewLoader == null) {
+ mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
+ }
+ return mWidgetPreviewLoader;
+ }
+} \ No newline at end of file