From 3f471440a8b6b71d4c15501a96befd3b715c9e8f Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 8 Apr 2015 19:01:34 -0700 Subject: WidgetTray revamp work - RecyclerView is rendering - Animation is connected - Drag and drop is now handled - UI tweaking: background, margins, more to come. - Flicker and preview not loading issue: fixed height for the horizontal scroll view. - Shortcuts are added - Widget Preview loading should support shortPress for drop - UI tweaks left: overlay of arrow when there are more items to scroll - icons are added in the section header - Sorting of widget sections and widget horizontal list - Adding all the padding constants to dimen.xml file - RecyclerView should only support one view type For items to be addressed in follow up patches OR CLs, TODO is added to the comment. b/19897708 Change-Id: Ibfc4da1696a23d20bada93db46e126706eb13cdc --- src/com/android/launcher3/AppInfo.java | 13 +- .../android/launcher3/AppsCustomizePagedView.java | 1060 -------------------- .../android/launcher3/AppsCustomizeTabHost.java | 228 ----- src/com/android/launcher3/CellLayout.java | 4 +- src/com/android/launcher3/DeleteDropTarget.java | 15 +- src/com/android/launcher3/DeviceProfile.java | 62 +- src/com/android/launcher3/DragSource.java | 2 +- src/com/android/launcher3/FastBitmapDrawable.java | 4 +- src/com/android/launcher3/IconCache.java | 12 +- src/com/android/launcher3/Insettable.java | 4 + src/com/android/launcher3/ItemInfo.java | 14 +- src/com/android/launcher3/Launcher.java | 118 +-- src/com/android/launcher3/LauncherAppState.java | 2 +- .../launcher3/LauncherAppWidgetProviderInfo.java | 15 +- src/com/android/launcher3/LauncherModel.java | 2 +- src/com/android/launcher3/LauncherSettings.java | 8 +- .../LauncherStateTransitionAnimation.java | 56 +- src/com/android/launcher3/PagedViewGridLayout.java | 121 --- src/com/android/launcher3/PagedViewWidget.java | 295 ------ .../launcher3/PagedViewWidgetImageView.java | 49 - .../launcher3/PagedViewWithDraggableItems.java | 174 ---- src/com/android/launcher3/PendingAddItemInfo.java | 88 +- src/com/android/launcher3/WidgetPreviewLoader.java | 16 +- .../android/launcher3/WidgetsContainerView.java | 84 -- src/com/android/launcher3/Workspace.java | 2 + .../android/launcher3/widget/PackageItemInfo.java | 57 ++ .../launcher3/widget/PendingAddShortcutInfo.java | 44 + .../launcher3/widget/PendingAddWidgetInfo.java | 91 ++ src/com/android/launcher3/widget/WidgetCell.java | 338 +++++++ .../android/launcher3/widget/WidgetImageView.java | 48 + .../launcher3/widget/WidgetsContainerView.java | 376 +++++++ .../launcher3/widget/WidgetsListAdapter.java | 188 ++++ src/com/android/launcher3/widget/WidgetsModel.java | 136 +++ .../android/launcher3/widget/WidgetsRowView.java | 90 ++ .../launcher3/widget/WidgetsRowViewHolder.java | 36 + 35 files changed, 1533 insertions(+), 2319 deletions(-) delete mode 100644 src/com/android/launcher3/AppsCustomizePagedView.java delete mode 100644 src/com/android/launcher3/AppsCustomizeTabHost.java delete mode 100644 src/com/android/launcher3/PagedViewGridLayout.java delete mode 100644 src/com/android/launcher3/PagedViewWidget.java delete mode 100644 src/com/android/launcher3/PagedViewWidgetImageView.java delete mode 100644 src/com/android/launcher3/PagedViewWithDraggableItems.java delete mode 100644 src/com/android/launcher3/WidgetsContainerView.java create mode 100644 src/com/android/launcher3/widget/PackageItemInfo.java create mode 100644 src/com/android/launcher3/widget/PendingAddShortcutInfo.java create mode 100644 src/com/android/launcher3/widget/PendingAddWidgetInfo.java create mode 100644 src/com/android/launcher3/widget/WidgetCell.java create mode 100644 src/com/android/launcher3/widget/WidgetImageView.java create mode 100644 src/com/android/launcher3/widget/WidgetsContainerView.java create mode 100644 src/com/android/launcher3/widget/WidgetsListAdapter.java create mode 100644 src/com/android/launcher3/widget/WidgetsModel.java create mode 100644 src/com/android/launcher3/widget/WidgetsRowView.java create mode 100644 src/com/android/launcher3/widget/WidgetsRowViewHolder.java (limited to 'src/com/android') diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index a1391b232..7c6b0664c 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -43,7 +43,7 @@ public class AppInfo extends ItemInfo { /** * A bitmap version of the application icon. */ - Bitmap iconBitmap; + public Bitmap iconBitmap; /** * Indicates whether we're using a low res icon @@ -55,7 +55,7 @@ public class AppInfo extends ItemInfo { */ long firstInstallTime; - ComponentName componentName; + public ComponentName componentName; static final int DOWNLOADED_FLAG = 1; static final int UPDATED_SYSTEM_APP_FLAG = 2; @@ -121,12 +121,15 @@ public class AppInfo extends ItemInfo { + " user=" + user + ")"; } + /** + * Helper method used for debugging. + */ public static void dumpApplicationInfoList(String tag, String label, ArrayList list) { Log.d(tag, label + " size=" + list.size()); for (AppInfo info: list) { - Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" - + info.iconBitmap + " firstInstallTime=" - + info.firstInstallTime); + Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap + + " firstInstallTime=" + info.firstInstallTime + + " componentName=" + info.componentName.getPackageName()); } } diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java deleted file mode 100644 index 58bcf1dbe..000000000 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ /dev/null @@ -1,1060 +0,0 @@ -/* - * 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; - -import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.GridLayout; -import android.widget.ImageView; -import android.widget.Toast; - -import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.FocusHelper.PagedViewKeyListener; -import com.android.launcher3.compat.AppWidgetManagerCompat; -import com.android.launcher3.util.Thunk; - -import java.util.ArrayList; - -/** - * The Apps/Customize page that displays all the applications, widgets, and shortcuts. - */ -public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements - View.OnClickListener, DragSource, - PagedViewWidget.ShortPressListener, LauncherTransitionable { - static final String TAG = "AppsCustomizePagedView"; - - private static Rect sTmpRect = new Rect(); - private static final int[] sTempPosArray = new int[2]; - - /** - * The different content types that this paged view can show. - */ - public enum ContentType { - Widgets - } - private ContentType mContentType = ContentType.Widgets; - - // Refs - @Thunk Launcher mLauncher; - private DragController mDragController; - private final LayoutInflater mLayoutInflater; - private final PackageManager mPackageManager; - - // Save and Restore - private int mSaveInstanceStateItemIndex = -1; - - // Content - private ArrayList mWidgets; - - // Caching - private IconCache mIconCache; - - // Dimens - private int mContentWidth, mContentHeight; - @Thunk int mWidgetCountX, mWidgetCountY; - private int mNumWidgetPages; - - private final PagedViewKeyListener mKeyListener = new PagedViewKeyListener(); - - private Runnable mInflateWidgetRunnable = null; - private Runnable mBindWidgetRunnable = null; - static final int WIDGET_NO_CLEANUP_REQUIRED = -1; - static final int WIDGET_PRELOAD_PENDING = 0; - static final int WIDGET_BOUND = 1; - static final int WIDGET_INFLATED = 2; - int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; - int mWidgetLoadingId = -1; - PendingAddWidgetInfo mCreateWidgetInfo = null; - private boolean mDraggingWidget = false; - boolean mPageBackgroundsVisible = true; - - private Toast mWidgetInstructionToast; - - // Deferral of loading widget previews during launcher transitions - private boolean mInTransition; - - WidgetPreviewLoader mWidgetPreviewLoader; - - private boolean mInBulkBind; - private boolean mNeedToUpdatePageCountsAndInvalidateData; - - public AppsCustomizePagedView(Context context, AttributeSet attrs) { - super(context, attrs); - mLayoutInflater = LayoutInflater.from(context); - mPackageManager = context.getPackageManager(); - mWidgets = new ArrayList<>(); - mIconCache = (LauncherAppState.getInstance()).getIconCache(); - - // Save the default widget preview background - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0); - mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2); - mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2); - a.recycle(); - - // The padding on the non-matched dimension for the default widget preview icons - // (top + bottom) - mFadeInAdjacentScreens = false; - - // Unless otherwise specified this view is important for accessibility. - if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - setSinglePageInViewport(); - } - - @Override - protected void init() { - super.init(); - mCenterPagesVertically = false; - - Context context = getContext(); - Resources r = context.getResources(); - setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f); - } - - public void onFinishInflate() { - super.onFinishInflate(); - - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setPadding(grid.edgeMarginPx, 2 * grid.edgeMarginPx, - grid.edgeMarginPx, 2 * grid.edgeMarginPx); - } - - void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) { - setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight); - } - - WidgetPreviewLoader getWidgetPreviewLoader() { - if (mWidgetPreviewLoader == null) { - mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache(); - } - return mWidgetPreviewLoader; - } - - /** Returns the item index of the center item on this page so that we can restore to this - * item index when we rotate. */ - private int getMiddleComponentIndexOnCurrentPage() { - int i = -1; - if (getPageCount() > 0) { - int currentPage = getCurrentPage(); - if (mContentType == ContentType.Widgets) { - PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage); - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - int childCount = layout.getChildCount(); - if (childCount > 0) { - i = (currentPage * numItemsPerPage) + (childCount / 2); - } - } else { - throw new RuntimeException("Invalid ContentType"); - } - } - return i; - } - - /** Get the index of the item to restore to if we need to restore the current page. */ - int getSaveInstanceStateIndex() { - if (mSaveInstanceStateItemIndex == -1) { - mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage(); - } - return mSaveInstanceStateItemIndex; - } - - /** Returns the page in the current orientation which is expected to contain the specified - * item index. */ - int getPageForComponent(int index) { - if (index < 0) return 0; - - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - return index / numItemsPerPage; - } - - /** Restores the page for an item at the specified index */ - void restorePageForIndex(int index) { - if (index < 0) return; - mSaveInstanceStateItemIndex = index; - } - - private void updatePageCounts() { - mNumWidgetPages = (int) Math.ceil(mWidgets.size() / - (float) (mWidgetCountX * mWidgetCountY)); - } - - protected void onDataReady(int width, int height) { - updatePageCounts(); - - // Force a measure to update recalculate the gaps - mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); - mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); - - final boolean hostIsTransitioning = getTabHost().isInTransition(); - int page = getPageForComponent(mSaveInstanceStateItemIndex); - invalidatePageData(Math.max(0, page), hostIsTransitioning); - } - - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - - if (!isDataReady()) { - if (!mWidgets.isEmpty()) { - post(new Runnable() { - // This code triggers requestLayout so must be posted outside of the - // layout pass. - public void run() { - if (Utilities.isViewAttachedToWindow(AppsCustomizePagedView.this)) { - setDataIsReady(); - onDataReady(getMeasuredWidth(), getMeasuredHeight()); - } - } - }); - } - } - } - - public void onPackagesUpdated(ArrayList widgetsAndShortcuts) { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - - // Get the list of widgets and shortcuts - mWidgets.clear(); - for (Object o : widgetsAndShortcuts) { - if (o instanceof LauncherAppWidgetProviderInfo) { - LauncherAppWidgetProviderInfo widget = (LauncherAppWidgetProviderInfo) o; - if (!app.shouldShowAppOrWidgetProvider(widget.provider) && !widget.isCustomWidget) { - continue; - } - - if (widget.minSpanX > 0 && widget.minSpanY > 0) { - // Ensure that all widgets we show can be added on a workspace of this size - int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget); - int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget); - int minSpanX = Math.min(spanXY[0], minSpanXY[0]); - int minSpanY = Math.min(spanXY[1], minSpanXY[1]); - if (minSpanX <= (int) grid.numColumns && - minSpanY <= (int) grid.numRows) { - mWidgets.add(widget); - } else { - Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" + - widget.minWidth + ", " + widget.minHeight + ")"); - } - } else { - Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" + - widget.minWidth + ", " + widget.minHeight + ")"); - } - } else { - // just add shortcuts - mWidgets.add(o); - } - } - - updatePageCountsAndInvalidateData(); - } - - public void setBulkBind(boolean bulkBind) { - if (bulkBind) { - mInBulkBind = true; - } else { - mInBulkBind = false; - if (mNeedToUpdatePageCountsAndInvalidateData) { - updatePageCountsAndInvalidateData(); - } - } - } - - private void updatePageCountsAndInvalidateData() { - if (mInBulkBind) { - mNeedToUpdatePageCountsAndInvalidateData = true; - } else { - updatePageCounts(); - invalidateOnDataChange(); - mNeedToUpdatePageCountsAndInvalidateData = false; - } - } - - @Override - public void onClick(View v) { - // When we have exited all apps or are in transition, disregard clicks - if (!mLauncher.isWidgetsViewVisible() - || mLauncher.getWorkspace().isSwitchingState() - || !(v instanceof PagedViewWidget)) 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(); - } - - /* - * PagedViewWithDraggableItems implementation - */ - @Override - protected void determineDraggingStart(android.view.MotionEvent ev) { - } - - static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { - Bundle options = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, sTmpRect); - Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher, - info.componentName, null); - - float density = launcher.getResources().getDisplayMetrics().density; - int xPaddingDips = (int) ((padding.left + padding.right) / density); - int yPaddingDips = (int) ((padding.top + padding.bottom) / density); - - options = new Bundle(); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, - sTmpRect.left - xPaddingDips); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, - sTmpRect.top - yPaddingDips); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, - sTmpRect.right - xPaddingDips); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, - sTmpRect.bottom - yPaddingDips); - } - return options; - } - - private void preloadWidget(final PendingAddWidgetInfo info) { - final LauncherAppWidgetProviderInfo pInfo = info.info; - final Bundle options = pInfo.isCustomWidget ? null : - getDefaultOptionsForWidget(mLauncher, info); - - if (pInfo.configure != null) { - info.bindOptions = options; - return; - } - - mWidgetCleanupState = WIDGET_PRELOAD_PENDING; - mBindWidgetRunnable = new Runnable() { - @Override - public void run() { - if (pInfo.isCustomWidget) { - mWidgetCleanupState = WIDGET_BOUND; - return; - } - - mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId(); - if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed( - mWidgetLoadingId, pInfo, options)) { - mWidgetCleanupState = WIDGET_BOUND; - } - - } - }; - post(mBindWidgetRunnable); - - mInflateWidgetRunnable = new Runnable() { - @Override - public void run() { - if (mWidgetCleanupState != WIDGET_BOUND) { - return; - } - AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView( - getContext(), mWidgetLoadingId, pInfo); - info.boundWidget = hostView; - mWidgetCleanupState = WIDGET_INFLATED; - hostView.setVisibility(INVISIBLE); - int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false); - - // We want the first widget layout to be the correct size. This will be important - // for width size reporting to the AppWidgetManager. - DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0], - unScaledSize[1]); - lp.x = lp.y = 0; - lp.customPosition = true; - hostView.setLayoutParams(lp); - mLauncher.getDragLayer().addView(hostView); - } - }; - post(mInflateWidgetRunnable); - } - - @Override - public void onShortPress(View v) { - // We are anticipating a long press, and we use this time to load bind and instantiate - // the widget. This will need to be cleaned up if it turns out no long press occurs. - if (mCreateWidgetInfo != null) { - // Just in case the cleanup process wasn't properly executed. This shouldn't happen. - cleanupWidgetPreloading(false); - } - mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag()); - preloadWidget(mCreateWidgetInfo); - } - - private void cleanupWidgetPreloading(boolean widgetWasAdded) { - if (!widgetWasAdded) { - // If the widget was not added, we may need to do further cleanup. - PendingAddWidgetInfo info = mCreateWidgetInfo; - mCreateWidgetInfo = null; - - if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) { - // We never did any preloading, so just remove pending callbacks to do so - removeCallbacks(mBindWidgetRunnable); - removeCallbacks(mInflateWidgetRunnable); - } else if (mWidgetCleanupState == WIDGET_BOUND) { - // Delete the widget id which was allocated - if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); - } - - // We never got around to inflating the widget, so remove the callback to do so. - removeCallbacks(mInflateWidgetRunnable); - } else if (mWidgetCleanupState == WIDGET_INFLATED) { - // Delete the widget id which was allocated - if (mWidgetLoadingId != -1 && !info.isCustomWidget()) { - mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId); - } - - // The widget was inflated and added to the DragLayer -- remove it. - AppWidgetHostView widget = info.boundWidget; - mLauncher.getDragLayer().removeView(widget); - } - } - mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED; - mWidgetLoadingId = -1; - mCreateWidgetInfo = null; - PagedViewWidget.resetShortPressTarget(); - } - - @Override - public void cleanUpShortPress(View v) { - if (!mDraggingWidget) { - cleanupWidgetPreloading(false); - } - } - - private boolean beginDraggingWidget(PagedViewWidget 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. - if (mCreateWidgetInfo == null) { - return false; - } - - PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo; - createItemInfo = createWidgetInfo; - 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.shortcutActivityInfo); - 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; - } - - @Override - protected boolean beginDragging(final View v) { - if (!super.beginDragging(v)) return false; - - if (v instanceof PagedViewWidget) { - if (!beginDraggingWidget((PagedViewWidget) 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; - } - - /** - * Clean up after dragging. - * - * @param target where the item was dragged to (can be null if the item was flung) - */ - private void endDragging(View target, 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); - } else { - mLauncher.unlockScreenOrientation(false); - } - } - - @Override - public View getContent() { - if (getChildCount() > 0) { - return getChildAt(0); - } - return null; - } - - @Override - public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - mInTransition = true; - if (toWorkspace) { - cancelAllTasks(false); - } - } - - @Override - public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { - } - - @Override - public void onLauncherTransitionStep(Launcher l, float t) { - } - - @Override - public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - mInTransition = false; - mForceDrawAllChildrenNextFrame = !toWorkspace; - if (!toWorkspace) { - loadPreviewsForPage(getNextPage()); - } - } - - @Override - public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, - boolean success) { - // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling - if (isFlingToDelete) return; - - endDragging(target, false, success); - - // 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; - } - cleanupWidgetPreloading(success); - mDraggingWidget = false; - } - - @Override - public void onFlingToDeleteCompleted() { - // We just dismiss the drag when we fling, so cleanup here - endDragging(null, true, true); - cleanupWidgetPreloading(false); - mDraggingWidget = false; - } - - @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override - public boolean supportsAppInfoDropTarget() { - return true; - } - - @Override - public boolean supportsDeleteDropTarget() { - return false; - } - - @Override - public float getIntrinsicIconScaleFactor() { - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - return (float) grid.allAppsIconSizePx / grid.iconSizePx; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - cancelAllTasks(true); - } - - @Override - public void trimMemory() { - super.trimMemory(); - cancelAllTasks(true); - } - - private void cancelAllTasks(boolean clearCompletedTasks) { - for (int page = getPageCount() - 1; page >= 0; page--) { - final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - if (layout != null) { - for (int i = 0; i < layout.getChildCount(); i++) { - ((PagedViewWidget) layout.getChildAt(i)).deletePreview(clearCompletedTasks); - } - } - } - } - - - public void setContentType(ContentType type) { - // Widgets appear to be cleared every time you leave, always force invalidate for them - if (mContentType != type || type == ContentType.Widgets) { - int page = (mContentType != type) ? 0 : getCurrentPage(); - mContentType = type; - invalidatePageData(page, true); - } - } - - public ContentType getContentType() { - return mContentType; - } - - public void setPageBackgroundsVisible(boolean visible) { - mPageBackgroundsVisible = visible; - int childCount = getChildCount(); - for (int i = 0; i < childCount; ++i) { - Drawable bg = getChildAt(i).getBackground(); - if (bg != null) { - bg.setAlpha(visible ? 255 : 0); - } - } - } - - /* - * Widgets PagedView implementation - */ - private void setupPage(PagedViewGridLayout layout) { - // Note: We force a measure here to get around the fact that when we do layout calculations - // immediately after syncing, we don't have a proper width. - int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); - int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); - - Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel_dark); - if (bg != null) { - bg.setAlpha(mPageBackgroundsVisible ? 255 : 0); - layout.setBackground(bg); - } - layout.measure(widthSpec, heightSpec); - } - - public void syncWidgetPageItems(final int page, final boolean immediate) { - int numItemsPerPage = mWidgetCountX * mWidgetCountY; - - final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - - // Calculate the dimensions of each cell we are giving to each widget - final ArrayList items = new ArrayList(); - int contentWidth = mContentWidth - layout.getPaddingLeft() - layout.getPaddingRight(); - final int cellWidth = contentWidth / mWidgetCountX; - int contentHeight = mContentHeight - layout.getPaddingTop() - layout.getPaddingBottom(); - - final int cellHeight = contentHeight / mWidgetCountY; - - // Prepare the set of widgets to load previews for in the background - int offset = page * numItemsPerPage; - for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) { - items.add(mWidgets.get(i)); - } - - // Prepopulate the pages with the other widget info, and fill in the previews later - layout.setColumnCount(layout.getCellCountX()); - for (int i = 0; i < items.size(); ++i) { - Object rawInfo = items.get(i); - PendingAddItemInfo createItemInfo = null; - PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( - R.layout.apps_customize_widget, layout, false); - - if (rawInfo instanceof LauncherAppWidgetProviderInfo) { - // Fill in the widget information - LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) rawInfo; - createItemInfo = new PendingAddWidgetInfo(info, null); - - widget.applyFromAppWidgetProviderInfo(info, -1, getWidgetPreviewLoader()); - widget.setTag(createItemInfo); - widget.setShortPressListener(this); - } else if (rawInfo instanceof ResolveInfo) { - // Fill in the shortcuts information - ResolveInfo info = (ResolveInfo) rawInfo; - createItemInfo = new PendingAddShortcutInfo(info.activityInfo); - createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; - createItemInfo.componentName = new ComponentName(info.activityInfo.packageName, - info.activityInfo.name); - widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader()); - widget.setTag(createItemInfo); - } - - widget.setOnClickListener(this); - widget.setOnLongClickListener(this); - widget.setOnTouchListener(this); - widget.setOnKeyListener(mKeyListener); - - // Layout each widget - int ix = i % mWidgetCountX; - int iy = i / mWidgetCountX; - - if (ix > 0) { - View border = widget.findViewById(R.id.left_border); - border.setVisibility(View.VISIBLE); - } - if (ix < mWidgetCountX - 1) { - View border = widget.findViewById(R.id.right_border); - border.setVisibility(View.VISIBLE); - } - - GridLayout.LayoutParams lp = new GridLayout.LayoutParams( - GridLayout.spec(iy, GridLayout.START), - GridLayout.spec(ix, GridLayout.TOP)); - lp.width = cellWidth; - lp.height = cellHeight; - lp.setGravity(Gravity.TOP | Gravity.START); - layout.addView(widget, lp); - } - - if (immediate && !mInTransition) { - loadPreviewsForPage(page); - } - } - - private void loadPreviewsForPage(int page) { - final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); - - if (layout != null) { - for (int i = 0; i < layout.getChildCount(); i++) { - ((PagedViewWidget) layout.getChildAt(i)).ensurePreview(); - } - } - } - - @Override - public void syncPages() { - disablePagedViewAnimations(); - - removeAllViews(); - cancelAllTasks(true); - - Context context = getContext(); - if (mContentType == ContentType.Widgets) { - for (int j = 0; j < mNumWidgetPages; ++j) { - PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX, - mWidgetCountY); - setupPage(layout); - addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT)); - } - } else { - throw new RuntimeException("Invalid ContentType"); - } - - enablePagedViewAnimations(); - } - - @Override - public void syncPageItems(int page, boolean immediate) { - if (mContentType == ContentType.Widgets) { - syncWidgetPageItems(page, immediate); - } else { - Log.e(TAG, "Unexpected ContentType"); - } - } - - // We want our pages to be z-ordered such that the further a page is to the left, the higher - // it is in the z-order. This is important to insure touch events are handled correctly. - View getPageAt(int index) { - return getChildAt(indexToPage(index)); - } - - @Override - protected int indexToPage(int index) { - return getChildCount() - index - 1; - } - - // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. - @Override - protected void screenScrolled(int screenCenter) { - super.screenScrolled(screenCenter); - enableHwLayersOnVisiblePages(); - } - - private void enableHwLayersOnVisiblePages() { - final int screenCount = getChildCount(); - - getVisiblePages(mTempVisiblePagesRange); - int leftScreen = mTempVisiblePagesRange[0]; - int rightScreen = mTempVisiblePagesRange[1]; - int forceDrawScreen = -1; - if (leftScreen == rightScreen) { - // make sure we're caching at least two pages always - if (rightScreen < screenCount - 1) { - rightScreen++; - forceDrawScreen = rightScreen; - } else if (leftScreen > 0) { - leftScreen--; - forceDrawScreen = leftScreen; - } - } else { - forceDrawScreen = leftScreen + 1; - } - - for (int i = 0; i < screenCount; i++) { - final View layout = (View) getPageAt(i); - if (!(leftScreen <= i && i <= rightScreen && - (i == forceDrawScreen || shouldDrawChild(layout)))) { - layout.setLayerType(LAYER_TYPE_NONE, null); - } - } - - for (int i = 0; i < screenCount; i++) { - final View layout = (View) getPageAt(i); - - if (leftScreen <= i && i <= rightScreen && - (i == forceDrawScreen || shouldDrawChild(layout))) { - if (layout.getLayerType() != LAYER_TYPE_HARDWARE) { - layout.setLayerType(LAYER_TYPE_HARDWARE, null); - } - } - } - } - - protected void overScroll(float amount) { - dampedOverScroll(amount); - } - - /** - * Used by the parent to get the content width to set the tab bar to - * @return - */ - public int getPageContentWidth() { - return mContentWidth; - } - - @Override - protected void onPageEndMoving() { - super.onPageEndMoving(); - mForceDrawAllChildrenNextFrame = true; - // We reset the save index when we change pages so that it will be recalculated on next - // rotation - mSaveInstanceStateItemIndex = -1; - } - - @Override - protected void onPageBeginMoving() { - super.onPageBeginMoving(); - if (!mInTransition) { - getVisiblePages(sTempPosArray); - for (int i = sTempPosArray[0]; i <= sTempPosArray[1]; i++) { - loadPreviewsForPage(i); - } - } - } - - /* - * AllAppsView implementation - */ - public void setup(Launcher launcher, DragController dragController) { - mLauncher = launcher; - mDragController = dragController; - } - - /** - * We should call thise method whenever the core data changes (mWidgets) so that we can - * appropriately determine when to invalidate the PagedView page data. In cases where the data - * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the - * next onMeasure() pass, which will trigger an invalidatePageData() itself. - */ - private void invalidateOnDataChange() { - if (!isDataReady()) { - // The next layout pass will trigger data-ready if both widgets and apps are set, so - // request a layout to trigger the page data when ready. - requestLayout(); - } else { - cancelAllTasks(false); - invalidatePageData(); - } - } - - public void reset() { - // If we have reset, then we should not continue to restore the previous state - mSaveInstanceStateItemIndex = -1; - - if (mContentType != ContentType.Widgets) { - setContentType(ContentType.Widgets); - } - - if (mCurrentPage != 0) { - invalidatePageData(0); - } - } - - private AppsCustomizeTabHost getTabHost() { - return (AppsCustomizeTabHost) mLauncher.findViewById(R.id.apps_customize_pane); - } - - public void dumpState() { - // TODO: Dump information related to current list of Applications, Widgets, etc. - dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets); - } - - private void dumpAppWidgetProviderInfoList(String tag, String label, - ArrayList list) { - Log.d(tag, label + " size=" + list.size()); - for (Object i: list) { - if (i instanceof AppWidgetProviderInfo) { - AppWidgetProviderInfo info = (AppWidgetProviderInfo) i; - Log.d(tag, " label=\"" + info.label + "\" previewImage=" + info.previewImage - + " resizeMode=" + info.resizeMode + " configure=" + info.configure - + " initialLayout=" + info.initialLayout - + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight); - } else if (i instanceof ResolveInfo) { - ResolveInfo info = (ResolveInfo) i; - Log.d(tag, " label=\"" + info.loadLabel(mPackageManager) + "\" icon=" - + info.icon); - } - } - } - - public void surrender() { - // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we - // should stop this now. - - // Stop all background tasks - cancelAllTasks(true); - } - - /* - * We load an extra page on each side to prevent flashes from scrolling and loading of the - * widget previews in the background with the AsyncTasks. - */ - final static int sLookBehindPageCount = 2; - final static int sLookAheadPageCount = 2; - protected int getAssociatedLowerPageBound(int page) { - final int count = getChildCount(); - int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1); - int windowMinIndex = Math.max(Math.min(page - sLookBehindPageCount, count - windowSize), 0); - return windowMinIndex; - } - protected int getAssociatedUpperPageBound(int page) { - final int count = getChildCount(); - int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1); - int windowMaxIndex = Math.min(Math.max(page + sLookAheadPageCount, windowSize - 1), - count - 1); - return windowMaxIndex; - } - - protected String getCurrentPageDescription() { - int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; - int stringId = R.string.default_scroll_format; - int count = 0; - - if (mContentType == ContentType.Widgets) { - stringId = R.string.apps_customize_widgets_scroll_format; - count = mNumWidgetPages; - } else { - throw new RuntimeException("Invalid ContentType"); - } - - return String.format(getContext().getString(stringId), page + 1, count); - } -} diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java deleted file mode 100644 index 5e2f05c61..000000000 --- a/src/com/android/launcher3/AppsCustomizeTabHost.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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; - -import android.content.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; -import android.widget.FrameLayout; - -public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable { - static final String LOG_TAG = "AppsCustomizeTabHost"; - - private static final String WIDGETS_TAB_TAG = "WIDGETS"; - - private AppsCustomizePagedView mPagedView; - private View mContent; - private boolean mInTransition = false; - - private final Rect mInsets = new Rect(); - - public AppsCustomizeTabHost(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Convenience methods to select specific tabs. We want to set the content type immediately - * in these cases, but we note that we still call setCurrentTabByTag() so that the tab view - * reflects the new content (but doesn't do the animation and logic associated with changing - * tabs manually). - */ - void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) { - mPagedView.setContentType(type); - } - - @Override - public void setInsets(Rect insets) { - mInsets.set(insets); - LayoutParams flp = (LayoutParams) mContent.getLayoutParams(); - flp.topMargin = insets.top; - flp.bottomMargin = insets.bottom; - flp.leftMargin = insets.left; - flp.rightMargin = insets.right; - mContent.setLayoutParams(flp); - } - - /** - * Setup the tab host and create all necessary tabs. - */ - @Override - protected void onFinishInflate() { - mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content); - mContent = findViewById(R.id.content); - } - - public String getContentTag() { - return getTabTagForContentType(mPagedView.getContentType()); - } - - /** - * Returns the content view used for the launcher transitions. - */ - public View getContentView() { - return findViewById(R.id.apps_customize_pane_content); - } - - /** - * Returns the reveal view used for the launcher transitions. - */ - public View getRevealView() { - return findViewById(R.id.fake_page); - } - - /** - * Returns the page indicators view. - */ - public View getPageIndicators() { - return findViewById(R.id.apps_customize_page_indicator); - } - - /** - * Returns the content type for the specified tab tag. - */ - public AppsCustomizePagedView.ContentType getContentTypeForTabTag(String tag) { - return AppsCustomizePagedView.ContentType.Widgets; - } - - /** - * Returns the tab tag for a given content type. - */ - public String getTabTagForContentType(AppsCustomizePagedView.ContentType type) { - return WIDGETS_TAB_TAG; - } - - /** - * Disable focus on anything under this view in the hierarchy if we are not visible. - */ - @Override - public int getDescendantFocusability() { - if (getVisibility() != View.VISIBLE) { - return ViewGroup.FOCUS_BLOCK_DESCENDANTS; - } - return super.getDescendantFocusability(); - } - - void reset() { - // Reset immediately - mPagedView.reset(); - } - - void trimMemory() { - mPagedView.trimMemory(); - } - - public void onWindowVisible() { - if (getVisibility() == VISIBLE) { - mContent.setVisibility(VISIBLE); - // We unload the widget previews when the UI is hidden, so need to reload pages - // Load the current page synchronously, and the neighboring pages asynchronously - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true); - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); - } - } - @Override - public ViewGroup getContent() { - return mPagedView; - } - - public boolean isInTransition() { - return mInTransition; - } - - /* LauncherTransitionable overrides */ - @Override - public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace); - mInTransition = true; - - if (toWorkspace) { - // Going from All Apps -> Workspace - setVisibilityOfSiblingsWithLowerZOrder(VISIBLE); - } else { - // Going from Workspace -> All Apps - mContent.setVisibility(VISIBLE); - - // Make sure the current page is loaded (we start loading the side pages after the - // transition to prevent slowing down the animation) - // TODO: revisit this - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); - } - } - - @Override - public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { - mPagedView.onLauncherTransitionStart(l, animated, toWorkspace); - } - - @Override - public void onLauncherTransitionStep(Launcher l, float t) { - mPagedView.onLauncherTransitionStep(l, t); - } - - @Override - public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace); - mInTransition = false; - - if (!toWorkspace) { - // Make sure adjacent pages are loaded (we wait until after the transition to - // prevent slowing down the animation) - mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); - - // Opening apps, need to announce what page we are on. - AccessibilityManager am = (AccessibilityManager) - getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - if (am.isEnabled()) { - // Notify the user when the page changes - announceForAccessibility(mPagedView.getCurrentPageDescription()); - } - - // Going from Workspace -> All Apps - // NOTE: We should do this at the end since we check visibility state in some of the - // cling initialization/dismiss code above. - setVisibilityOfSiblingsWithLowerZOrder(INVISIBLE); - } - } - - private void setVisibilityOfSiblingsWithLowerZOrder(int visibility) { - ViewGroup parent = (ViewGroup) getParent(); - if (parent == null) return; - - View appsView = ((Launcher) getContext()).getAppsView(); - View overviewPanel = ((Launcher) getContext()).getOverviewPanel(); - final int count = parent.getChildCount(); - if (!isChildrenDrawingOrderEnabled()) { - for (int i = 0; i < count; i++) { - final View child = parent.getChildAt(i); - if (child == this) { - break; - } else { - if (child.getVisibility() == GONE || child == overviewPanel || - child == appsView) { - continue; - } - child.setVisibility(visibility); - } - } - } else { - throw new RuntimeException("Failed; can't get z-order of views"); - } - } -} diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 63afa3091..f4afb954d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -45,7 +45,6 @@ import android.util.Log; import android.util.SparseArray; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; @@ -56,6 +55,7 @@ import android.view.animation.LayoutAnimationController; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.LauncherAccessibilityDelegate.DragType; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.ArrayList; import java.util.Arrays; @@ -3025,7 +3025,7 @@ public class CellLayout extends ViewGroup { * * @return True if a vacant cell of the specified dimension was found, false otherwise. */ - boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { + public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) { return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null, mOccupied); } diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 1f0dad221..62aa285ab 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -42,6 +42,7 @@ import android.view.animation.LinearInterpolator; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.WidgetsContainerView; public class DeleteDropTarget extends ButtonDropTarget { private static int DELETE_ANIMATION_DURATION = 285; @@ -100,8 +101,9 @@ public class DeleteDropTarget extends ButtonDropTarget { private boolean isAllAppsApplication(DragSource source, Object info) { return source.supportsAppInfoDropTarget() && (info instanceof AppInfo); } - private boolean isAllAppsWidget(DragSource source, Object info) { - if (source instanceof AppsCustomizePagedView) { + + private boolean isWidget(DragSource source, Object info) { + if (source instanceof WidgetsContainerView) { if (info instanceof PendingAddItemInfo) { PendingAddItemInfo addInfo = (PendingAddItemInfo) info; switch (addInfo.itemType) { @@ -173,7 +175,7 @@ public class DeleteDropTarget extends ButtonDropTarget { // If we are dragging an application from AppsCustomize, only show the control if we can // delete the app (it was downloaded), and rename the string to "uninstall" in such a case. // Hide the delete target if it is a widget from AppsCustomize. - if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) { + if (!willAcceptDrop(info) || isWidget(source, info)) { isVisible = false; } if (useUninstallLabel) { @@ -489,13 +491,14 @@ public class DeleteDropTarget extends ButtonDropTarget { } public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) { - final boolean isAllApps = d.dragSource instanceof AppsCustomizePagedView; + final boolean isWidgets = d.dragSource instanceof WidgetsContainerView; + final boolean isAllapps = d.dragSource instanceof AppsContainerView; // Don't highlight the icon as it's animating d.dragView.setColor(0); d.dragView.updateInitialScaleToCurrentScale(); // Don't highlight the target if we are flinging from AllApps - if (isAllApps) { + if (isWidgets || isAllapps) { resetHoverColor(); } @@ -545,7 +548,7 @@ public class DeleteDropTarget extends ButtonDropTarget { public void run() { // If we are dragging from AllApps, then we allow AppsCustomizePagedView to clean up // itself, otherwise, complete the drop to initiate the deletion process - if (!isAllApps) { + if (!isWidgets || !isAllapps) { mLauncher.exitSpringLoadedDragMode(); completeDrop(d); } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 331695acc..ea2852080 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -106,8 +106,8 @@ public class DeviceProfile { public int cellWidthPx; public int cellHeightPx; - int iconSizePx; - int iconTextSizePx; + public int iconSizePx; + public int iconTextSizePx; int iconDrawablePaddingPx; int allAppsIconSizePx; int allAppsIconTextSizePx; @@ -803,64 +803,6 @@ public class DeviceProfile { } } - // Layout AllApps - AppsCustomizeTabHost host = (AppsCustomizeTabHost) - launcher.findViewById(R.id.apps_customize_pane); - if (host != null) { - // Center the all apps page indicator - int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f, - (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX))); - pageIndicator = host.findViewById(R.id.apps_customize_page_indicator); - if (pageIndicator != null) { - LinearLayout.LayoutParams lllp = (LinearLayout.LayoutParams) pageIndicator.getLayoutParams(); - lllp.width = LayoutParams.WRAP_CONTENT; - lllp.height = pageIndicatorHeight; - pageIndicator.setLayoutParams(lllp); - } - - AppsCustomizePagedView pagedView = (AppsCustomizePagedView) - host.findViewById(R.id.apps_customize_pane_content); - - FrameLayout fakePageContainer = (FrameLayout) - host.findViewById(R.id.fake_page_container); - FrameLayout fakePage = (FrameLayout) host.findViewById(R.id.fake_page); - - padding = new Rect(); - if (pagedView != null) { - // Constrain the dimensions of all apps so that it does not span the full width - int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) / - (2 * (allAppsNumCols + 1)); - int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) / - (2 * (allAppsNumRows + 1)); - paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f)); - paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f)); - int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR)); - int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2; - // Only adjust the side paddings on landscape phones, or tablets - if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) { - padding.left = padding.right = gridPaddingLR; - } - - // The icons are centered, so we can't just offset by the page indicator height - // because the empty space will actually be pageIndicatorHeight + paddingTB - padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB); - - pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight); - fakePage.setBackground(res.getDrawable(R.drawable.quantum_panel)); - - // Horizontal padding for the whole paged view - int pagedFixedViewPadding = - res.getDimensionPixelSize(R.dimen.apps_customize_horizontal_padding); - - padding.left += pagedFixedViewPadding; - padding.right += pagedFixedViewPadding; - - pagedView.setPadding(padding.left, padding.top, padding.right, padding.bottom); - fakePageContainer.setPadding(padding.left, padding.top, padding.right, padding.bottom); - - } - } - // Layout the Overview Mode ViewGroup overviewMode = launcher.getOverviewPanel(); if (overviewMode != null) { diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java index 7369eeac2..2a1346ef5 100644 --- a/src/com/android/launcher3/DragSource.java +++ b/src/com/android/launcher3/DragSource.java @@ -22,9 +22,9 @@ import com.android.launcher3.DropTarget.DragObject; /** * Interface defining an object that can originate a drag. - * */ public interface DragSource { + /** * @return whether items dragged from this source supports */ diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index ff02bbbc3..28e923e67 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -32,7 +32,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.SparseArray; -class FastBitmapDrawable extends Drawable { +public class FastBitmapDrawable extends Drawable { static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() { @@ -72,7 +72,7 @@ class FastBitmapDrawable extends Drawable { private boolean mPressed = false; private ObjectAnimator mPressedAnimator; - FastBitmapDrawable(Bitmap b) { + public FastBitmapDrawable(Bitmap b) { mAlpha = 255; mBitmap = b; setBounds(0, 0, b.getWidth(), b.getHeight()); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index f4af7f542..f6238dab2 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -44,6 +44,7 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.PackageItemInfo; import java.util.HashMap; import java.util.HashSet; @@ -454,12 +455,13 @@ public class IconCache { * Fill in {@param appInfo} with the icon and label for {@param packageName} */ public synchronized void getTitleAndIconForApp( - String packageName, UserHandleCompat user, boolean useLowResIcon, AppInfo appInfoOut) { + String packageName, UserHandleCompat user, boolean useLowResIcon, + PackageItemInfo infoOut) { CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon); - appInfoOut.iconBitmap = entry.icon; - appInfoOut.title = entry.title; - appInfoOut.usingLowResIcon = entry.isLowResIcon; - appInfoOut.contentDescription = entry.contentDescription; + infoOut.iconBitmap = entry.icon; + infoOut.title = entry.title; + infoOut.usingLowResIcon = entry.isLowResIcon; + infoOut.contentDescription = entry.contentDescription; } public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { diff --git a/src/com/android/launcher3/Insettable.java b/src/com/android/launcher3/Insettable.java index 1d2356c65..3b8ef2f93 100644 --- a/src/com/android/launcher3/Insettable.java +++ b/src/com/android/launcher3/Insettable.java @@ -18,6 +18,10 @@ package com.android.launcher3; import android.graphics.Rect; +/** + * Allows the implementing {@link View} to not draw underneath system bars. + * e.g., notification bar on top and home key area on the bottom. + */ public interface Insettable { void setInsets(Rect insets); diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index f114de221..f7e0ea488 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -36,7 +36,7 @@ public class ItemInfo { */ static final String EXTRA_PROFILE = "profile"; - static final int NO_ID = -1; + public static final int NO_ID = -1; /** * The id in the settings database for this item @@ -82,7 +82,7 @@ public class ItemInfo { /** * Indicates the Y cell span. */ - int spanY = 1; + public int spanY = 1; /** * Indicates the minimum X cell span. @@ -107,21 +107,21 @@ public class ItemInfo { /** * Title of the item */ - CharSequence title; + public CharSequence title; /** * Content description of the item. */ - CharSequence contentDescription; + public CharSequence contentDescription; /** * The position of the item in a drag-and-drop operation. */ - int[] dropPos = null; + public int[] dropPos = null; - UserHandleCompat user; + public UserHandleCompat user; - ItemInfo() { + public ItemInfo() { user = UserHandleCompat.myUserHandle(); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2fa2f4ad7..068934e1b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -103,6 +103,8 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.launcher3.widget.WidgetsContainerView; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -130,11 +132,11 @@ public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener, LauncherStateTransitionAnimation.Callbacks { - static final String TAG = "Launcher"; - static final boolean LOGD = false; + static final String TAG = "Launcher - MERONG"; + static final boolean LOGD = true; static final boolean PROFILE_STARTUP = false; - static final boolean DEBUG_WIDGETS = false; + static final boolean DEBUG_WIDGETS = true; static final boolean DEBUG_STRICT_MODE = false; static final boolean DEBUG_RESUME_TIME = false; static final boolean DEBUG_DUMP_LOG = false; @@ -264,9 +266,13 @@ public class Launcher extends Activity private View mAllAppsButton; private SearchDropTargetBar mSearchDropTargetBar; + + // Main container view for the all apps screen. @Thunk AppsContainerView mAppsView; - @Thunk AppsCustomizeTabHost mAppsCustomizeTabHost; - private AppsCustomizePagedView mAppsCustomizeContent; + + // Main container view for the widget tray screen. + private WidgetsContainerView mWidgetsView; + private boolean mAutoAdvanceRunning = false; private AppWidgetHostView mQsb; @@ -672,7 +678,7 @@ public class Launcher extends Activity return mInflater; } - boolean isDraggingEnabled() { + public boolean isDraggingEnabled() { // We prevent dragging when we are loading the workspace as it is possible to pick up a view // that is subsequently removed from the workspace in startBinding(). return !mModel.isLoadingWorkspace(); @@ -1013,15 +1019,9 @@ public class Launcher extends Activity startTimeCallbacks = System.currentTimeMillis(); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setBulkBind(true); - } for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) { mBindOnResumeCallbacks.get(i).run(); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.setBulkBind(false); - } mBindOnResumeCallbacks.clear(); if (DEBUG_RESUME_TIME) { Log.d(TAG, "Time spent processing callbacks in onResume: " + @@ -1213,9 +1213,8 @@ public class Launcher extends Activity if (mModel.isCurrentCallbacks(this)) { mModel.stopLoader(); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.surrender(); - } + //TODO(hyunyoungs): stop the widgets loader when there is a rotation. + return Boolean.TRUE; } @@ -1336,19 +1335,6 @@ public class Launcher extends Activity mRestoring = true; } - // Restore the AppsCustomize tab - if (mAppsCustomizeTabHost != null) { - String curTab = savedState.getString("apps_customize_currentTab"); - if (curTab != null) { - mAppsCustomizeTabHost.setContentTypeImmediate( - mAppsCustomizeTabHost.getContentTypeForTabTag(curTab)); - mAppsCustomizeContent.loadAssociatedPages( - mAppsCustomizeContent.getCurrentPage()); - } - - int currentIndex = savedState.getInt("apps_customize_currentIndex"); - mAppsCustomizeContent.restorePageForIndex(currentIndex); - } mItemIdToViewId = (HashMap) savedState.getSerializable(RUNTIME_STATE_VIEW_IDS); } @@ -1434,10 +1420,7 @@ public class Launcher extends Activity mAppsView = (AppsContainerView) findViewById(R.id.apps_view); // Setup AppsCustomize - mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane); - mAppsCustomizeContent = (AppsCustomizePagedView) - mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content); - mAppsCustomizeContent.setup(this, dragController); + mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view); // Setup the drag controller (drop targets have to be added in reverse order in priority) dragController.setDragScoller(mWorkspace); @@ -1651,7 +1634,7 @@ public class Launcher extends Activity // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop - if (mAppsView != null && mAppsCustomizeTabHost != null && + if (mAppsView != null && mWidgetsView != null && mPendingAddInfo.container == ItemInfo.NO_ID) { showWorkspace(false); } @@ -1735,7 +1718,6 @@ public class Launcher extends Activity // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged // is a more appropriate event to handle if (mVisible) { - mAppsCustomizeTabHost.onWindowVisible(); if (!mWorkspaceLoading) { final ViewTreeObserver observer = mWorkspace.getViewTreeObserver(); // We want to let Launcher draw itself at least once before we force it to build @@ -1839,7 +1821,7 @@ public class Launcher extends Activity launcherInfo.hostView = null; } - void showOutOfSpaceMessage(boolean isHotseatLayout) { + public void showOutOfSpaceMessage(boolean isHotseatLayout) { int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space); Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show(); } @@ -1852,8 +1834,8 @@ public class Launcher extends Activity return mAppsView; } - public AppsCustomizeTabHost getWidgetsView() { - return mAppsCustomizeTabHost; + public WidgetsContainerView getWidgetsView() { + return mWidgetsView; } public Workspace getWorkspace() { @@ -1946,9 +1928,9 @@ public class Launcher extends Activity mAppsView.scrollToTop(); } - // Reset the apps customize page - if (!alreadyOnHome && mAppsCustomizeTabHost != null) { - mAppsCustomizeTabHost.reset(); + // Reset the widgets view + if (!alreadyOnHome && mWidgetsView != null) { + mWidgetsView.scrollToTop(); } if (mLauncherCallbacks != null) { @@ -2003,16 +1985,8 @@ public class Launcher extends Activity outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id); } - // Save the current AppsCustomize tab - if (mAppsCustomizeTabHost != null) { - AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType(); - String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type); - if (currentTabTag != null) { - outState.putString("apps_customize_currentTab", currentTabTag); - } - int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex(); - outState.putInt("apps_customize_currentIndex", currentIndex); - } + // Save the current widgets tray? + // TODO(hyunyoungs) outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId); if (mLauncherCallbacks != null) { @@ -3276,9 +3250,7 @@ public class Launcher extends Activity SQLiteDatabase.releaseMemory(); // This clears all widget bitmaps from the widget tray - if (mAppsCustomizeTabHost != null) { - mAppsCustomizeTabHost.trimMemory(); - } + // TODO(hyunyoungs) } if (mLauncherCallbacks != null) { mLauncherCallbacks.onTrimMemory(level); @@ -3355,15 +3327,16 @@ public class Launcher extends Activity * Shows the widgets view. */ void showWidgetsView(boolean animated, boolean resetPageToZero) { + Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero); if (resetPageToZero) { - mAppsCustomizeTabHost.reset(); + mWidgetsView.scrollToTop(); } showAppsOrWidgets(animated, State.WIDGETS); - mAppsCustomizeTabHost.post(new Runnable() { + + mWidgetsView.post(new Runnable() { @Override public void run() { - // We post this in-case the all apps view isn't yet constructed. - mAppsCustomizeTabHost.requestFocus(); + mWidgetsView.requestFocus(); } }); } @@ -3394,7 +3367,9 @@ public class Launcher extends Activity .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } - void enterSpringLoadedDragMode() { + public void enterSpringLoadedDragMode() { + Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", + mState.name())); if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED || mState == State.WIDGETS_SPRING_LOADED) { return; @@ -3405,7 +3380,7 @@ public class Launcher extends Activity mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; } - void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, + public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return; @@ -3413,10 +3388,12 @@ public class Launcher extends Activity @Override public void run() { if (successfulDrop) { + // TODO(hyunyoungs): verify if this hack is still needed, if not, delete. + // // Before we show workspace, hide all apps again because // exitSpringLoadedDragMode made it visible. This is a bit hacky; we should // clean up our state transition functions - mAppsCustomizeTabHost.setVisibility(View.GONE); + mWidgetsView.setVisibility(View.GONE); showWorkspace(true, onCompleteRunnable); } else { exitSpringLoadedDragMode(); @@ -3918,8 +3895,8 @@ public class Launcher extends Activity pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = - AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo); + Bundle options = null; + // AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo); int newWidgetId = mAppWidgetHost.allocateAppWidgetId(); boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( @@ -4122,9 +4099,9 @@ public class Launcher extends Activity if (mAppsView != null) { mAppsView.setApps(apps); } - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this, false /* refresh */)); + if (mWidgetsView != null) { + mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false), + getPackageManager()); } if (mLauncherCallbacks != null) { mLauncherCallbacks.bindAllApplications(apps); @@ -4276,15 +4253,16 @@ public class Launcher extends Activity mWidgetsAndShortcuts = null; } }; + public void bindPackagesUpdated(final ArrayList widgetsAndShortcuts) { if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) { mWidgetsAndShortcuts = widgetsAndShortcuts; return; } - // Update the widgets pane - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts); + if (mWidgetsView != null) { + mWidgetsView.addWidgets(LauncherModel.getSortedWidgetsAndShortcuts(this, false), + getPackageManager()); } } @@ -4577,10 +4555,8 @@ public class Launcher extends Activity Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState); Log.d(TAG, "sFolders.size=" + sFolders.size()); mModel.dumpState(); + // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState(); - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.dumpState(); - } Log.d(TAG, "END launcher3 dump state"); } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 2a08b8176..3bd385028 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -141,7 +141,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return mModel; } - LauncherAccessibilityDelegate getAccessibilityDelegate() { + public LauncherAccessibilityDelegate getAccessibilityDelegate() { return mAccessibilityDelegate; } diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index aeef0daeb..bb4580ce7 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -16,10 +16,10 @@ import android.os.Parcel; public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { public boolean isCustomWidget = false; - int spanX = -1; - int spanY = -1; - int minSpanX = -1; - int minSpanY = -1; + public int spanX = -1; + public int spanY = -1; + public int minSpanX = -1; + public int minSpanY = -1; public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context, AppWidgetProviderInfo info) { @@ -78,10 +78,11 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { return super.loadIcon(context, cache.getFullResIconDpi()); } - public String toString() { + public String toString(PackageManager pm) { if (isCustomWidget) { - return "LauncherAppWidgetProviderInfo(" + provider + ")"; + return "WidgetProviderInfo(" + provider + ")"; } - return super.toString(); + return String.format("WidgetProviderInfo provider:%s package:%s short:%s label:%s span(%d, %d) minSpan(%d, %d)", + provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm), spanX, spanY, minSpanX, minSpanY); } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 1f36331ec..98ba09bc6 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -3628,7 +3628,7 @@ public class LauncherModel extends BroadcastReceiver private final HashMap mLabelCache; private final Collator mCollator; - WidgetAndShortcutNameComparator(Context context) { + public WidgetAndShortcutNameComparator(Context context) { mManager = AppWidgetManagerCompat.getInstance(context); mPackageManager = context.getPackageManager(); mLabelCache = new HashMap(); diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index d657cb50f..111de409e 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -22,7 +22,7 @@ import android.provider.BaseColumns; /** * Settings related utilities. */ -class LauncherSettings { +public class LauncherSettings { /** Columns required on table staht will be subject to backup and restore. */ static interface ChangeLogColumns extends BaseColumns { /** @@ -121,7 +121,7 @@ class LauncherSettings { /** * Favorites. */ - static final class Favorites implements BaseLauncherColumns { + public static final class Favorites implements BaseLauncherColumns { /** * The content:// style URL for this table */ @@ -217,12 +217,12 @@ class LauncherSettings { /** * The favorite is a widget */ - static final int ITEM_TYPE_APPWIDGET = 4; + public static final int ITEM_TYPE_APPWIDGET = 4; /** * The favorite is a custom widget provided by the launcher */ - static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5; + public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5; /** * The favorite is a clock diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index eacf3415e..e92bfb053 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -23,6 +23,7 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.content.res.Resources; +import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewAnimationUtils; @@ -30,6 +31,7 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.WidgetsContainerView; import java.util.HashMap; @@ -179,20 +181,12 @@ public class LauncherStateTransitionAnimation { * Starts an animation to the widgets view. */ public void startAnimationToWidgets(final boolean animated) { - final AppsCustomizeTabHost toView = mLauncher.getWidgetsView(); + final WidgetsContainerView toView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - // Hide the real page background, and swap in the fake one - ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(false); - revealView.setBackground( - mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark)); - } - @Override - public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { - // Show the real page background - ((AppsCustomizePagedView) contentView).setPageBackgroundsVisible(true); + revealView.setBackground(mLauncher.getDrawable(R.drawable.quantum_panel_dark)); } @Override public float getMaterialRevealViewFinalAlpha(View revealView) { @@ -204,7 +198,7 @@ public class LauncherStateTransitionAnimation { } }; startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView, toView.getContentView(), - toView.getRevealView(), toView.getPageIndicators(), animated, cb); + toView.getRevealView(), null, animated, cb); } /** @@ -500,44 +494,8 @@ public class LauncherStateTransitionAnimation { private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState, final Workspace.State toWorkspaceState, final boolean animated, final Runnable onCompleteRunnable) { - AppsCustomizeTabHost widgetsView = mLauncher.getWidgetsView(); + WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { - @Override - public void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) { - AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); - - // Hide the real page background, and swap in the fake one - pagedView.stopScrolling(); - pagedView.setPageBackgroundsVisible(false); - revealView.setBackground( - mLauncher.getResources().getDrawable(R.drawable.quantum_panel_dark)); - - // Hide the side pages of the Widget tray to avoid some ugly edge cases - final View currentPage = pagedView.getPageAt(pagedView.getNextPage()); - int count = pagedView.getChildCount(); - for (int i = 0; i < count; i++) { - View child = pagedView.getChildAt(i); - if (child != currentPage) { - child.setVisibility(View.INVISIBLE); - } - } - } - @Override - public void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) { - AppsCustomizePagedView pagedView = ((AppsCustomizePagedView) contentView); - - // Show the real page background and force-update the page - pagedView.setPageBackgroundsVisible(true); - pagedView.setCurrentPage(pagedView.getNextPage()); - pagedView.updateCurrentPageScroll(); - - // Unhide the side pages - int count = pagedView.getChildCount(); - for (int i = 0; i < count; i++) { - View child = pagedView.getChildAt(i); - child.setVisibility(View.VISIBLE); - } - } @Override public float getMaterialRevealViewFinalYDrift(View revealView) { return revealView.getMeasuredHeight() / 2; @@ -559,7 +517,7 @@ public class LauncherStateTransitionAnimation { }; startAnimationToWorkspaceFromOverlay(toWorkspaceState, widgetsView, widgetsView.getContentView(), widgetsView.getRevealView(), - widgetsView.getPageIndicators(), animated, onCompleteRunnable, cb); + null, animated, onCompleteRunnable, cb); } /** diff --git a/src/com/android/launcher3/PagedViewGridLayout.java b/src/com/android/launcher3/PagedViewGridLayout.java deleted file mode 100644 index f69fa562d..000000000 --- a/src/com/android/launcher3/PagedViewGridLayout.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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; - -import android.content.Context; -import android.view.MotionEvent; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.GridLayout; - -/** - * The grid based layout used strictly for the widget/wallpaper tab of the AppsCustomize pane - */ -public class PagedViewGridLayout extends GridLayout implements Page { - static final String TAG = "PagedViewGridLayout"; - - private int mCellCountX; - private int mCellCountY; - private Runnable mOnLayoutListener; - - public PagedViewGridLayout(Context context, int cellCountX, int cellCountY) { - super(context, null, 0); - mCellCountX = cellCountX; - mCellCountY = cellCountY; - } - - int getCellCountX() { - return mCellCountX; - } - - int getCellCountY() { - return mCellCountY; - } - - /** - * 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 onDetachedFromWindow() { - super.onDetachedFromWindow(); - mOnLayoutListener = null; - } - - public void setOnLayoutListener(Runnable r) { - mOnLayoutListener = r; - } - - 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); - int count = getPageChildCount(); - if (count > 0) { - // We only intercept the touch if we are tapping in empty space after the final row - View child = getChildOnPageAt(count - 1); - int bottom = child.getBottom(); - result = result || (event.getY() < bottom); - } - return result; - } - - @Override - public void removeAllViewsOnPage() { - removeAllViews(); - mOnLayoutListener = null; - setLayerType(LAYER_TYPE_NONE, null); - } - - @Override - public void removeViewOnPageAt(int index) { - removeViewAt(index); - } - - @Override - public int getPageChildCount() { - return getChildCount(); - } - - @Override - public View getChildOnPageAt(int i) { - return getChildAt(i); - } - - @Override - public int indexOfChildOnPage(View v) { - return indexOfChild(v); - } - - public static class LayoutParams extends FrameLayout.LayoutParams { - public LayoutParams(int width, int height) { - super(width, height); - } - } -} diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java deleted file mode 100644 index d9ca7be87..000000000 --- a/src/com/android/launcher3/PagedViewWidget.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2010 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.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.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.WidgetPreviewLoader.PreviewLoadRequest; -import com.android.launcher3.compat.AppWidgetManagerCompat; - -/** - * The linear layout used strictly for the widget/wallpaper tab of the customization tray - */ -public class PagedViewWidget extends LinearLayout implements OnLayoutChangeListener { - - private static PagedViewWidget 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 PagedViewWidget(Context context) { - this(context, null); - } - - public PagedViewWidget(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - final Resources r = context.getResources(); - mDimensionsFormatString = r.getString(R.string.widget_dims_format); - - 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() { - super.onDetachedFromWindow(); - deletePreview(true); - } - - 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] = i.getWidth() - mOriginalImagePadding.left - mOriginalImagePadding.right; - maxSize[1] = i.getHeight() - mOriginalImagePadding.top; - return maxSize; - } - - public void applyPreview(Bitmap bitmap) { - FastBitmapDrawable preview = new FastBitmapDrawable(bitmap); - final PagedViewWidgetImageView image = - (PagedViewWidgetImageView) findViewById(R.id.widget_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; - } - } - - 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(PagedViewWidget.this); - sShortpressTarget = PagedViewWidget.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(PagedViewWidget.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 (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); - } -} diff --git a/src/com/android/launcher3/PagedViewWidgetImageView.java b/src/com/android/launcher3/PagedViewWidgetImageView.java deleted file mode 100644 index 7d8279547..000000000 --- a/src/com/android/launcher3/PagedViewWidgetImageView.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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; - -import android.content.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.widget.ImageView; - -public class PagedViewWidgetImageView extends ImageView { - public boolean mAllowRequestLayout = true; - - public PagedViewWidgetImageView(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/PagedViewWithDraggableItems.java b/src/com/android/launcher3/PagedViewWithDraggableItems.java deleted file mode 100644 index f0743cf1c..000000000 --- a/src/com/android/launcher3/PagedViewWithDraggableItems.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2010 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.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - - -/* Class that does most of the work of enabling dragging items out of a PagedView by performing a - * vertical drag. Used by both CustomizePagedView and AllAppsPagedView. - * Subclasses must do the following: - * * call setDragSlopeThreshold after making an instance of the PagedViewWithDraggableItems - * * call child.setOnLongClickListener(this) and child.setOnTouchListener(this) on all children - * (good place to do it is in syncPageItems) - * * override beginDragging(View) (but be careful to call super.beginDragging(View) - * - */ -public abstract class PagedViewWithDraggableItems extends PagedView - implements View.OnLongClickListener, View.OnTouchListener { - private View mLastTouchedItem; - private boolean mIsDragging; - private boolean mIsDragEnabled; - private float mDragSlopeThreshold; - private Launcher mLauncher; - - public PagedViewWithDraggableItems(Context context) { - this(context, null); - } - - public PagedViewWithDraggableItems(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagedViewWithDraggableItems(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mLauncher = (Launcher) context; - } - - protected boolean beginDragging(View v) { - boolean wasDragging = mIsDragging; - mIsDragging = true; - return !wasDragging; - } - - protected void cancelDragging() { - mIsDragging = false; - mLastTouchedItem = null; - mIsDragEnabled = false; - } - - private void handleTouchEvent(MotionEvent ev) { - final int action = ev.getAction(); - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - cancelDragging(); - mIsDragEnabled = true; - break; - case MotionEvent.ACTION_MOVE: - if (mTouchState != TOUCH_STATE_SCROLLING && !mIsDragging && mIsDragEnabled) { - determineDraggingStart(ev); - } - break; - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - handleTouchEvent(ev); - return super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - handleTouchEvent(ev); - return super.onTouchEvent(ev); - } - - public void trimMemory() { - mLastTouchedItem = null; - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - mLastTouchedItem = v; - mIsDragEnabled = true; - return false; - } - - @Override - public boolean onLongClick(View v) { - // Return early if this is not initiated from a touch - if (!v.isInTouchMode()) return false; - // Return early if we are still animating the pages - if (mNextPage != INVALID_PAGE) 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 - if (!mLauncher.isDraggingEnabled()) return false; - - return beginDragging(v); - } - - /* - * Determines if we should change the touch state to start scrolling after the - * user moves their touch point too far. - */ - protected void determineScrollingStart(MotionEvent ev) { - if (!mIsDragging) super.determineScrollingStart(ev); - } - - /* - * Determines if we should change the touch state to start dragging after the - * user moves their touch point far enough. - */ - protected void determineDraggingStart(MotionEvent ev) { - /* - * Locally do absolute value. mLastMotionX is set to the y value - * of the down event. - */ - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - final float x = ev.getX(pointerIndex); - final float y = ev.getY(pointerIndex); - final int xDiff = (int) Math.abs(x - mLastMotionX); - final int yDiff = (int) Math.abs(y - mLastMotionY); - - final int touchSlop = mTouchSlop; - boolean yMoved = yDiff > touchSlop; - boolean isUpwardMotion = (yDiff / (float) xDiff) > mDragSlopeThreshold; - - if (isUpwardMotion && yMoved && mLastTouchedItem != null) { - // Drag if the user moved far enough along the Y axis - beginDragging(mLastTouchedItem); - - // Cancel any pending long press - if (mAllowLongPress) { - mAllowLongPress = false; - // Try canceling the long press. It could also have been scheduled - // by a distant descendant, so use the mAllowLongPress flag to block - // everything - final View currentPage = getPageAt(mCurrentPage); - if (currentPage != null) { - currentPage.cancelLongPress(); - } - } - } - } - - public void setDragSlopeThreshold(float dragSlopeThreshold) { - mDragSlopeThreshold = dragSlopeThreshold; - } - - @Override - protected void onDetachedFromWindow() { - cancelDragging(); - super.onDetachedFromWindow(); - } -} diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java index ac54a262f..1aaf85bbd 100644 --- a/src/com/android/launcher3/PendingAddItemInfo.java +++ b/src/com/android/launcher3/PendingAddItemInfo.java @@ -16,93 +16,17 @@ package com.android.launcher3; -import android.appwidget.AppWidgetHostView; import android.content.ComponentName; -import android.content.pm.ActivityInfo; -import android.os.Bundle; -import android.os.Parcelable; /** - * We pass this object with a drag from the customization tray + * Meta data that is used for deferred binding. + * e.g., this object is used to pass information on dragable targets when they are dropped onto + * the workspace from another container. */ -class PendingAddItemInfo extends ItemInfo { +public class PendingAddItemInfo extends ItemInfo { + /** * The component that will be created. */ - ComponentName componentName; -} - -class PendingAddShortcutInfo extends PendingAddItemInfo { - - ActivityInfo shortcutActivityInfo; - - public PendingAddShortcutInfo(ActivityInfo activityInfo) { - shortcutActivityInfo = activityInfo; - } - - @Override - public String toString() { - return "Shortcut: " + shortcutActivityInfo.packageName; - } -} - -class PendingAddWidgetInfo extends PendingAddItemInfo { - int minWidth; - int minHeight; - int minResizeWidth; - int minResizeHeight; - int previewImage; - int icon; - LauncherAppWidgetProviderInfo info; - AppWidgetHostView boundWidget; - 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 "Widget: " + componentName.toShortString(); - } + public ComponentName componentName; } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 1043e2ee0..5c3ed9272 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -32,6 +32,7 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Thunk; +import com.android.launcher3.widget.WidgetCell; import java.lang.ref.WeakReference; import java.util.Collections; @@ -45,6 +46,7 @@ import java.util.concurrent.ExecutionException; public class WidgetPreviewLoader { private static final String TAG = "WidgetPreviewLoader"; + private static final boolean DEBUG = false; private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f; @@ -78,7 +80,7 @@ public class WidgetPreviewLoader { * @return a request id which can be used to cancel the request. */ public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight, - PagedViewWidget caller, Bitmap[] immediateResult) { + WidgetCell caller, Bitmap[] immediateResult) { String size = previewWidth + "x" + previewHeight; WidgetCacheKey key = getObjectKey(o, size); @@ -576,21 +578,26 @@ public class WidgetPreviewLoader { private final Object mInfo; private final int mPreviewHeight; private final int mPreviewWidth; - private final PagedViewWidget mCaller; + private final WidgetCell mCaller; PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, - int previewHeight, PagedViewWidget caller) { + int previewHeight, WidgetCell caller) { mKey = key; mInfo = info; mPreviewHeight = previewHeight; mPreviewWidth = previewWidth; mCaller = caller; + if (DEBUG) { + Log.d(TAG, String.format("%s, %s, %d, %d", + mKey, mInfo, mPreviewHeight, mPreviewWidth)); + } } - @Override protected Bitmap doInBackground(Void... params) { Bitmap unusedBitmap = null; + + // TODO(hyunyoungs): Figure out why this path causes concurrency issue. synchronized (mUnusedBitmaps) { // Check if we can use a bitmap for (Bitmap candidate : mUnusedBitmaps) { @@ -608,7 +615,6 @@ public class WidgetPreviewLoader { mUnusedBitmaps.remove(unusedBitmap); } } - if (isCancelled()) { return null; } diff --git a/src/com/android/launcher3/WidgetsContainerView.java b/src/com/android/launcher3/WidgetsContainerView.java deleted file mode 100644 index 7004d8b29..000000000 --- a/src/com/android/launcher3/WidgetsContainerView.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.android.launcher3; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.FrameLayout; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - - -class SectionedWidgetsRow { - String section; - List> widgets; - - public SectionedWidgetsRow(String sc) { - section = sc; - } -} - -class SectionedWidgetsAlgorithm { - public List computeSectionedWidgetRows(List sortedWidgets, - int widgetsPerRow) { - List rows = new ArrayList<>(); - LinkedHashMap> sections = computeSectionedApps(sortedWidgets); - for (Map.Entry> sectionEntry : sections.entrySet()) { - String section = sectionEntry.getKey(); - SectionedWidgetsRow row = new SectionedWidgetsRow(section); - List widgets = sectionEntry.getValue(); - int numRows = (int) Math.ceil((float) widgets.size() / widgetsPerRow); - for (int i = 0; i < numRows; i++) { - List widgetsInRow = new ArrayList<>(); - int offset = i * widgetsPerRow; - for (int j = 0; j < widgetsPerRow; j++) { - widgetsInRow.add(widgets.get(offset + j)); - } - row.widgets.add(widgetsInRow); - } - } - return rows; - } - - private LinkedHashMap> computeSectionedApps(List sortedWidgets) { - LinkedHashMap> sections = new LinkedHashMap<>(); - for (Object info : sortedWidgets) { - String section = getSection(info); - List sectionedWidgets = sections.get(section); - if (sectionedWidgets == null) { - sectionedWidgets = new ArrayList<>(); - sections.put(section, sectionedWidgets); - } - sectionedWidgets.add(info); - } - return sections; - } - - private String getSection(Object widgetOrShortcut) { - return "UNKNOWN"; - } -} - -/** - * The widgets list view container. - */ -public class WidgetsContainerView extends FrameLayout { - - - 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); - } - - @Override - protected void onFinishInflate() { - } -} diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a79add05f..8cc99a044 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -71,6 +71,8 @@ import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.WallpaperUtils; +import com.android.launcher3.widget.PendingAddShortcutInfo; +import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.ArrayList; import java.util.HashMap; 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 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. + * + *

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 { + + 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 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. + * + *

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 mPackageNames = new ArrayList<>(); + + private Map mPackageItemInfoList = new HashMap<>(); + + /* Map of widgets and shortcuts that are tracked per package. */ + private Map> 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 getSortedWidgets(String packageName) { + return mWidgetsList.get(packageName); + } + + public void addWidgetsAndShortcuts(ArrayList 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 widgetsShortcutsList = mWidgetsList.get(packageName); + if (widgetsShortcutsList != null) { + widgetsShortcutsList.add(o); + } else { + widgetsShortcutsList = new ArrayList(); + 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() { + @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; + } +} -- cgit v1.2.3