Java tutorial
/* * 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.zyk.launcher; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.pm.ApplicationInfo; 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.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Process; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.Toast; import com.zyk.launcher.alarm.OnAlarmListener; import com.zyk.launcher.compat.AppWidgetManagerCompat; import com.zyk.launcher.util.DragUtils; import com.zyk.launcher.util.Utils; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** * A simple callback interface which also provides the results of the task. */ interface AsyncTaskCallback { void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data); } /** * The data needed to perform either of the custom AsyncTasks. */ class AsyncTaskPageData { enum Type { LoadWidgetPreviewData } AsyncTaskPageData(int p, ArrayList<Object> l, int cw, int ch, AsyncTaskCallback bgR, AsyncTaskCallback postR, WidgetPreviewLoader w) { page = p; items = l; generatedImages = new ArrayList<Bitmap>(); maxImageWidth = cw; maxImageHeight = ch; doInBackgroundCallback = bgR; postExecuteCallback = postR; widgetPreviewLoader = w; } void cleanup(boolean cancelled) { // Clean up any references to source/generated bitmaps if (generatedImages != null) { if (cancelled) { for (int i = 0; i < generatedImages.size(); i++) { widgetPreviewLoader.recycleBitmap(items.get(i), generatedImages.get(i)); } } generatedImages.clear(); } } int page; ArrayList<Object> items; ArrayList<Bitmap> sourceImages; ArrayList<Bitmap> generatedImages; int maxImageWidth; int maxImageHeight; AsyncTaskCallback doInBackgroundCallback; AsyncTaskCallback postExecuteCallback; WidgetPreviewLoader widgetPreviewLoader; } /** * A generic template for an async task used in AppsCustomize. */ class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTaskPageData> { AppsCustomizeAsyncTask(int p, AsyncTaskPageData.Type ty) { page = p; threadPriority = Process.THREAD_PRIORITY_DEFAULT; dataType = ty; } @Override protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) { if (params.length != 1) return null; // Load each of the widget previews in the background params[0].doInBackgroundCallback.run(this, params[0]); return params[0]; } @Override protected void onPostExecute(AsyncTaskPageData result) { // All the widget previews are loaded, so we can just callback to inflate the page result.postExecuteCallback.run(this, result); } void setThreadPriority(int p) { threadPriority = p; } void syncThreadPriority() { Process.setThreadPriority(threadPriority); } // The page that this async task is associated with AsyncTaskPageData.Type dataType; int page; int threadPriority; } /** * The Apps/Customize page that displays all the applications, widgets, and shortcuts. * FIXME * ? ?? ?? ???? workspace? */ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements View.OnClickListener, View.OnKeyListener, DragSource, DropTarget, DragScroller, PagedViewWidget.ShortPressListener, LauncherTransitionable, DragController.DragListener { static final String TAG = "AppsCustomizePagedView"; private static Rect sTmpRect = new Rect(); /** * The different content types that this paged view can show. * showAllAppsapp ? * applicating app * Widgets ? */ public enum ContentType { Applications, Widgets } private ContentType mContentType = ContentType.Applications; // Refs // private Launcher mLauncher; private DragController mDragController; private final LayoutInflater mLayoutInflater; private final PackageManager mPackageManager; // Save and Restore private int mSaveInstanceStateItemIndex = -1; // Content private ArrayList<ItemInfo> mApps; private ArrayList<Object> mWidgets; // Caching private IconCache mIconCache; // Dimens private int mContentWidth, mContentHeight; private int mWidgetCountX, mWidgetCountY; private PagedViewCellLayout mWidgetSpacingLayout; private int mNumAppsPages; private int mNumWidgetPages; private Rect mAllAppsPadding = new Rect(); // Previews & outlines ArrayList<AppsCustomizeAsyncTask> mRunningTasks; private static final int sPageSleepDelay = 200; 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; private ArrayList<AsyncTaskPageData> mDeferredSyncWidgetPageItems = new ArrayList<AsyncTaskPageData>(); private ArrayList<Runnable> mDeferredPrepareLoadWidgetPreviewsTasks = new ArrayList<Runnable>(); WidgetPreviewLoader mWidgetPreviewLoader; private boolean mInBulkBind; private boolean mNeedToUpdatePageCountsAndInvalidateData; private float mMaxDistanceForFolderCreation; private float mTransitionProgress; //?????????? private boolean syncPagesEnable = true; private List<Integer> tempScreens = new ArrayList<Integer>(); boolean mAnimatingViewIntoPlace = false; boolean mChildrenLayersEnabled = true; private Runnable mDelayedResizeRunnable; // State variable that indicates whether the pages are small (ie when you're // in all apps or customize mode) normal normal_hidden spring_loaded overview overview_hidden //SPRING_LOADED ?? public enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN }; public AppsCustomizePagedView(Context context, AttributeSet attrs) { super(context, attrs); mLayoutInflater = LayoutInflater.from(context); mPackageManager = context.getPackageManager(); mApps = new ArrayList<ItemInfo>(); mWidgets = new ArrayList<Object>(); mIconCache = (LauncherAppState.getInstance()).getIconCache(); mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>(); // 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(); mWidgetSpacingLayout = new PagedViewCellLayout(getContext()); // 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(); DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); mDragUtils = new DragUtils(mLauncher, this); mDragEnforcer = new DropTarget.DragEnforcer(context); } @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 setAllAppsPadding(Rect r) { mAllAppsPadding.set(r); } void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) { setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight); } WidgetPreviewLoader getWidgetPreviewLoader() { if (mWidgetPreviewLoader == null) { mWidgetPreviewLoader = new WidgetPreviewLoader(mLauncher); } 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.Applications) { AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(currentPage); ShortcutAndWidgetContainer childrenLayout = layout.getShortcutsAndWidgets(); int numItemsPerPage = mCellCountX * mCellCountY; int childCount = childrenLayout.getChildCount(); if (childCount > 0) { i = (currentPage * numItemsPerPage) + (childCount / 2); } } else if (mContentType == ContentType.Widgets) { int numApps = mApps.size(); PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage); int numItemsPerPage = mWidgetCountX * mWidgetCountY; int childCount = layout.getChildCount(); if (childCount > 0) { i = numApps + (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; if (index < mApps.size()) { int numItemsPerPage = mCellCountX * mCellCountY; return (index / numItemsPerPage); } else { int numItemsPerPage = mWidgetCountX * mWidgetCountY; return (index - mApps.size()) / 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)); mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY)); } protected void onDataReady(int width, int height) { // Now that the data is ready, we can calculate the content width, the number of cells to // use for each page LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); mCellCountX = (int) grid.allAppsNumCols; mCellCountY = (int) grid.allAppsNumRows; updatePageCounts(); // Force a measure to update recalculate the gaps mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); mWidgetSpacingLayout.measure(widthSpec, heightSpec); 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 ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty())) {// && !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 setOnDataReady() { if (!isDataReady()) { //?? mApps // if ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty())) {// && !mWidgets.isEmpty() if (Utilities.isViewAttachedToWindow(AppsCustomizePagedView.this)) { setDataIsReady(); onDataReady(getMeasuredWidth(), getMeasuredHeight()); } // } } } public void onPackagesUpdated(ArrayList<Object> 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 AppWidgetProviderInfo) { AppWidgetProviderInfo widget = (AppWidgetProviderInfo) o; if (!app.shouldShowAppOrWidgetProvider(widget.provider)) { continue; } if (widget.minWidth > 0 && widget.minHeight > 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.isAllAppsVisible() || 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(); // Create a little animation to show that the widget can move float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY); final ImageView p = (ImageView) v.findViewById(R.id.widget_preview); AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet(); ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY); tyuAnim.setDuration(125); ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f); tydAnim.setDuration(100); bounce.play(tyuAnim).before(tydAnim); bounce.setInterpolator(new AccelerateInterpolator()); bounce.start(); } @Override public boolean onLongClick(View v) { System.out.println("appsCustomizePagedView onLongClick AppsCustomizePagedView"); if (isLongClicked) { CellLayout.CellInfo longClickCellInfo = null; View itemUnderLongClick = null; if (v.getTag() instanceof ItemInfo) { ItemInfo info = (ItemInfo) v.getTag(); longClickCellInfo = new CellLayout.CellInfo(v, info); itemUnderLongClick = longClickCellInfo.cell; // resetAddInfo(); } final boolean inHotseat = mLauncher.isHotseatLayout(v); // boolean allowLongPress = inHotseat || mWorkspace.allowLongPress(); boolean allowLongPress = inHotseat || true; if (allowLongPress && !mDragController.isDragging()) { if (itemUnderLongClick == null) { // User long pressed on empty space System.out.println("User long pressed on empty space"); // mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, // HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); // if (mWorkspace.isInOverviewMode()) { // mWorkspace.startReordering(v); // } else { // mWorkspace.enterOverviewMode(); // } } else { final boolean isAllAppsButton = inHotseat && mLauncher.isAllAppsButtonRank(mLauncher .getHotseat().getOrderInHotseat(longClickCellInfo.cellX, longClickCellInfo.cellY)); if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) { // User long pressed on an item mLauncher.getAllAppsButton().setIsEnable(true); startDrag(longClickCellInfo); } } } // startDrag(longClickCellInfo); isLongClicked = false; return true; } else { return super.onLongClick(v); } } public boolean onKey(View v, int keyCode, KeyEvent event) { return FocusHelper.handleAppsCustomizeKeyEvent(v, keyCode, event); } /* * PagedViewWithDraggableItems implementation */ @Override protected void determineDraggingStart(android.view.MotionEvent ev) { // Disable dragging by pulling an app down for now. } private void beginDraggingApplication(View v) { mLauncher.getWorkspace().beginDragShared(v, this); } 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 AppWidgetProviderInfo pInfo = info.info; final Bundle options = getDefaultOptionsForWidget(mLauncher, info); if (pInfo.configure != null) { info.bindOptions = options; return; } mWidgetCleanupState = WIDGET_PRELOAD_PENDING; mBindWidgetRunnable = new Runnable() { @Override public void run() { 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.spanX, info.spanY, 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) { 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) { 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(View 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 spanX = createItemInfo.spanX; int spanY = createItemInfo.spanY; int[] size = mLauncher.getWorkspace().estimateItemSize(spanX, spanY, createWidgetInfo, true); FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable(); float minScale = 1.25f; int maxWidth, maxHeight; maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]); maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]); int[] previewSizeBeforeScale = new int[1]; preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info, spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale); // Compare the size of the drag preview to the preview in the AppsCustomize tray int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0], getWidgetPreviewLoader().maxWidthForWidgetPreview(spanX)); 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 BubbleTextView) { beginDraggingApplication(v); } else if (v instanceof PagedViewWidget) { if (!beginDraggingWidget(v)) { return false; } } // 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(); } } @Override public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { } @Override public void onLauncherTransitionStep(Launcher l, float t) { mTransitionProgress = t; } @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { mInTransition = false; for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) { onSyncWidgetPageItems(d, false); } mDeferredSyncWidgetPageItems.clear(); for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) { r.run(); } mDeferredPrepareLoadWidgetPreviewsTasks.clear(); mForceDrawAllChildrenNextFrame = !toWorkspace; } @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(); } @Override public void trimMemory() { super.trimMemory(); clearAllWidgetPages(); } public void clearAllWidgetPages() { cancelAllTasks(); int count = getChildCount(); for (int i = 0; i < count; i++) { View v = getPageAt(i); if (v instanceof PagedViewGridLayout) { ((PagedViewGridLayout) v).removeAllViewsOnPage(); mDirtyPageContent.set(i, true); } } } private void cancelAllTasks() { // Clean up all the async tasks Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); while (iter.hasNext()) { AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); task.cancel(false); iter.remove(); mDirtyPageContent.set(task.page, true); // We've already preallocated the views for the data to load into, so clear them as well View v = getPageAt(task.page); if (v instanceof PagedViewGridLayout) { ((PagedViewGridLayout) v).removeAllViewsOnPage(); } } mDeferredSyncWidgetPageItems.clear(); mDeferredPrepareLoadWidgetPreviewsTasks.clear(); } 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; } protected void snapToPage(int whichPage, int delta, int duration) { super.snapToPage(whichPage, delta, duration); // Update the thread priorities given the direction lookahead Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); while (iter.hasNext()) { AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); int pageIndex = task.page; if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) || (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) { task.setThreadPriority(getThreadPriorityForPage(pageIndex)); } else { task.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); } } } /* * Apps PagedView implementation */ private void setVisibilityOnChildren(ViewGroup layout, int visibility) { int childCount = layout.getChildCount(); for (int i = 0; i < childCount; ++i) { layout.getChildAt(i).setVisibility(visibility); } } private void setupPage(AppsCustomizeCellLayout layout) { layout.setGridSize(mCellCountX, mCellCountY); // 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. That said, we already know the // expected page width, so we can actually optimize by hiding all the TextView-based // children that are expensive to measure, and let that happen naturally later. setVisibilityOnChildren(layout, View.GONE); int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST); int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); layout.measure(widthSpec, heightSpec); Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel); if (bg != null) { bg.setAlpha(mPageBackgroundsVisible ? 255 : 0); // layout.setBackground(bg); layout.setBackgroundColor(Color.TRANSPARENT); } setVisibilityOnChildren(layout, View.VISIBLE); } 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); } } } public void syncAppsPageItems(int page) { ArrayList<ItemInfo> data = getPageData(page); int cv = data.size(); for (int i = 0; i < cv; i++) { System.out.println("LauncherModel i:" + page + " " + data.get(i)); } if (mLauncher.getModel().isFirst) { // ensure that we have the right number of items on the pages final boolean isRtl = isLayoutRtl(); int numCells = mCellCountX * mCellCountY; int startIndex = page * numCells; int endIndex = Math.min(startIndex + numCells, mApps.size()); AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page); layout.removeAllViewsOnPage(); ArrayList<Object> items = new ArrayList<Object>(); ArrayList<Bitmap> images = new ArrayList<Bitmap>(); for (int i = startIndex; i < endIndex; ++i) { AppInfo info = (AppInfo) data.get(i); View icon = createAllApp(R.layout.apps_customize_application, layout, info); int index = i - startIndex; int x = index % mCellCountX; int y = index / mCellCountX; if (isRtl) { x = mCellCountX - x - 1; } // layout.addViewToCellLayout(icon, -1, i, new CellLayout.LayoutParams(x, y, 1, 1), false); addInScreenFromBind(icon, page, x, y, info, layout); items.add(info); images.add(info.iconBitmap); info.screenId = page; info.cellX = x; info.cellY = y; info.rank = i; info.container = LauncherSettings.Allapps.CONTAINER_ALLAPPS; info.id = LauncherAppState.getLauncherProvider().getItemId(LauncherProvider.TABLE_ALLAPPS); LauncherModel.addAllAppsItemToDatabase(getContext(), info, false); } enableHwLayersOnVisiblePages(); } else { int count = data.size(); for (int i = 0; i < count; i++) { ItemInfo item = data.get(i); AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getScreenWithId(item.screenId); int x = i % mCellCountX; int y = i / mCellCountX; switch (item.itemType) { case LauncherSettings.Allapps.ITEM_TYPE_APPLICATION: case LauncherSettings.Allapps.ITEM_TYPE_SHORTCUT: AppInfo info = (AppInfo) item; View shortcut = createAllApp(R.layout.apps_customize_application, layout, info); // CellLayout cl = getScreenWithId(item.screenId); // if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { // View v = cl.getChildAt(item.cellX, item.cellY); // Object tag = v.getTag(); // String desc = "Collision while binding AllApps item: " + item // + ". Collides with " + tag; // if (LauncherAppState.isDogfoodBuild()) { // throw (new RuntimeException(desc)); // } else { // Log.d(TAG, desc); // } // } addInScreenFromBind(shortcut, item.screenId, x, y, info, layout); // layout.addViewToCellLayout(shortcut, -1, i, new CellLayout.LayoutParams(info.cellX, info.cellY, 1, 1), false); break; case LauncherSettings.Allapps.ITEM_TYPE_FOLDER: FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, layout, (FolderInfo) item, mIconCache); addInScreenFromBind(newFolder, item.screenId, x, y, item, layout); // layout.addViewToCellLayout(newFolder, -1, i, new CellLayout.LayoutParams(item.cellX, item.cellY, 1, 1), false); break; default: throw new RuntimeException("Invalid Item Type"); } } } } private Comparator<ItemInfo> comparator = new Comparator<ItemInfo>() { @Override public int compare(ItemInfo lhs, ItemInfo rhs) { int rankl = lhs.rank; int rankr = rhs.rank; if (rankl < 0 && rankr >= 0) { return rankr; } else if (rankr < 0 && rankl >= 0) { return rankl; } else if (rankl < 0 && rankr < 0) { return lhs.title.toString().compareTo(rhs.title.toString()); } else { return rankl - rankr; } } }; public ArrayList<ItemInfo> getPageData(int page) { if (page == 2) { System.out.println(""); } if (mLauncher.getModel().isFirst) { return mApps; } else { ArrayList<ItemInfo> data = new ArrayList<ItemInfo>(); int count = mApps.size(); for (int i = 0; i < count; i++) { ItemInfo item = mApps.get(i); if (item.screenId == page) { data.add(item); } } Collections.sort(data, comparator); return data; } } private View createAllApp(int layoutResId, AppsCustomizeCellLayout parent, AppInfo info) { BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(layoutResId, parent, false); icon.applyFromApplicationInfo(info, false); icon.setOnClickListener(mLauncher); icon.setOnLongClickListener(this); icon.setOnTouchListener(this); icon.setOnKeyListener(this); icon.setOnFocusChangeListener(parent.mFocusHandlerView); //FIXME ?? icon.setTextColor(Color.BLACK); //FIXME ?? ?? // ??? //layout icon.setBackgroundColor(Color.TRANSPARENT); return icon; } /** * A helper to return the priority for loading of the specified widget page. */ private int getWidgetPageLoadPriority(int page) { // If we are snapping to another page, use that index as the target page index int toPage = mCurrentPage; if (mNextPage > -1) { toPage = mNextPage; } // We use the distance from the target page as an initial guess of priority, but if there // are no pages of higher priority than the page specified, then bump up the priority of // the specified page. Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); int minPageDiff = Integer.MAX_VALUE; while (iter.hasNext()) { AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); minPageDiff = Math.abs(task.page - toPage); } int rawPageDiff = Math.abs(page - toPage); return rawPageDiff - Math.min(rawPageDiff, minPageDiff); } /** * Return the appropriate thread priority for loading for a given page (we give the current * page much higher priority) */ private int getThreadPriorityForPage(int page) { // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below int pageDiff = getWidgetPageLoadPriority(page); if (pageDiff <= 0) { return Process.THREAD_PRIORITY_LESS_FAVORABLE; } else if (pageDiff <= 1) { return Process.THREAD_PRIORITY_LOWEST; } else { return Process.THREAD_PRIORITY_LOWEST; } } private int getSleepForPage(int page) { int pageDiff = getWidgetPageLoadPriority(page); return Math.max(0, pageDiff * sPageSleepDelay); } /** * Creates and executes a new AsyncTask to load a page of widget previews. */ private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets, int cellWidth, int cellHeight, int cellCountX) { // Prune all tasks that are no longer needed Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); while (iter.hasNext()) { AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); int taskPage = task.page; if (taskPage < getAssociatedLowerPageBound(mCurrentPage) || taskPage > getAssociatedUpperPageBound(mCurrentPage)) { task.cancel(false); iter.remove(); } else { task.setThreadPriority(getThreadPriorityForPage(taskPage)); } } // We introduce a slight delay to order the loading of side pages so that we don't thrash final int sleepMs = getSleepForPage(page); AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight, new AsyncTaskCallback() { @Override public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { try { try { Thread.sleep(sleepMs); } catch (Exception e) { } loadWidgetPreviewsInBackground(task, data); } finally { if (task.isCancelled()) { data.cleanup(true); } } } }, new AsyncTaskCallback() { @Override public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { mRunningTasks.remove(task); if (task.isCancelled()) return; // do cleanup inside onSyncWidgetPageItems onSyncWidgetPageItems(data, false); } }, getWidgetPreviewLoader()); // Ensure that the task is appropriately prioritized and runs in parallel AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page, AsyncTaskPageData.Type.LoadWidgetPreviewData); t.setThreadPriority(getThreadPriorityForPage(page)); t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData); mRunningTasks.add(t); } /* * 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); } /** * Widget * @param page * @param immediate */ 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<Object> items = new ArrayList<Object>(); 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 AppWidgetProviderInfo) { // Fill in the widget information AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; createItemInfo = new PendingAddWidgetInfo(info, null, null); // Determine the widget spans and min resize spans. int[] spanXY = Launcher.getSpanForWidget(mLauncher, info); createItemInfo.spanX = spanXY[0]; createItemInfo.spanY = spanXY[1]; int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, info); createItemInfo.minSpanX = minSpanXY[0]; createItemInfo.minSpanY = minSpanXY[1]; widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, 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(this); // 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); } // wait until a call on onLayout to start loading, because // PagedViewWidget.getPreviewSize() will return 0 if it hasn't been laid out // TODO: can we do a measure/layout immediately? layout.setOnLayoutListener(new Runnable() { public void run() { // Load the widget previews int maxPreviewWidth = cellWidth; int maxPreviewHeight = cellHeight; if (layout.getChildCount() > 0) { PagedViewWidget w = (PagedViewWidget) layout.getChildAt(0); int[] maxSize = w.getPreviewSize(); maxPreviewWidth = maxSize[0]; maxPreviewHeight = maxSize[1]; } getWidgetPreviewLoader().setPreviewSize(maxPreviewWidth, maxPreviewHeight, mWidgetSpacingLayout); if (immediate) { AsyncTaskPageData data = new AsyncTaskPageData(page, items, maxPreviewWidth, maxPreviewHeight, null, null, getWidgetPreviewLoader()); loadWidgetPreviewsInBackground(null, data); onSyncWidgetPageItems(data, immediate); } else { if (mInTransition) { mDeferredPrepareLoadWidgetPreviewsTasks.add(this); } else { prepareLoadWidgetPreviewsTask(page, items, maxPreviewWidth, maxPreviewHeight, mWidgetCountX); } } layout.setOnLayoutListener(null); } }); } private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task, AsyncTaskPageData data) { // loadWidgetPreviewsInBackground can be called without a task to load a set of widget // previews synchronously if (task != null) { // Ensure that this task starts running at the correct priority task.syncThreadPriority(); } // Load each of the widget/shortcut previews ArrayList<Object> items = data.items; ArrayList<Bitmap> images = data.generatedImages; int count = items.size(); for (int i = 0; i < count; ++i) { if (task != null) { // Ensure we haven't been cancelled yet if (task.isCancelled()) break; // Before work on each item, ensure that this task is running at the correct // priority task.syncThreadPriority(); } images.add(getWidgetPreviewLoader().getPreview(items.get(i))); } } private void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) { if (!immediatelySyncItems && mInTransition) { mDeferredSyncWidgetPageItems.add(data); return; } try { int page = data.page; PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page); ArrayList<Object> items = data.items; int count = items.size(); for (int i = 0; i < count; ++i) { PagedViewWidget widget = (PagedViewWidget) layout.getChildAt(i); if (widget != null) { Bitmap preview = data.generatedImages.get(i); widget.applyPreview(new FastBitmapDrawable(preview), i); } } enableHwLayersOnVisiblePages(); // Update all thread priorities Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator(); while (iter.hasNext()) { AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next(); int pageIndex = task.page; task.setThreadPriority(getThreadPriorityForPage(pageIndex)); } } finally { data.cleanup(false); } } @Override public void syncPages() { //mLauncher.isAllAppInitEnable ??syncPagesEnable ???? if (mLauncher.getModel().isFirst && mLauncher.isAllAppInitEnable && syncPagesEnable) { syncPagesEnable = false; disablePagedViewAnimations(); removeAllViews(); cancelAllTasks(); Context context = getContext(); if (mContentType == ContentType.Applications) { for (int i = 0; i < mNumAppsPages; ++i) { AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(context); setupPage(layout); addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); ContentValues values = new ContentValues(); values.put(LauncherSettings.AllAppScreens._ID, i); values.put(LauncherSettings.AllAppScreens.SCREEN_RANK, i); mLauncher.getModel().insertNewAllAppScreens(getContext(), values); } } else 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 (mLauncher.getModel().isFirst && mLauncher.isAllAppInitEnable) { if (tempScreens.indexOf(page) > -1) { return; } tempScreens.add(page); if (mContentType == ContentType.Widgets) { syncWidgetPageItems(page, immediate); } else { syncAppsPageItems(page); } } } // 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. //FIXME View getPageAt(int index) { // return getChildAt(indexToPage(index)); return getChildAt(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(); } public 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; if (mDelayedResizeRunnable != null) { mDelayedResizeRunnable.run(); mDelayedResizeRunnable = null; } } /* * AllAppsView implementation */ public void setup(Launcher launcher, DragController dragController) { mLauncher = launcher; mDragController = dragController; } /** * We should call thise method whenever the core data changes (mApps, 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(); invalidatePageData(); } } public void setApps(ArrayList<ItemInfo> list) { if (!LauncherAppState.isDisableAllApps()) { mApps = list; Collections.sort(mApps, LauncherModel.getAppNameComparator()); updatePageCountsAndInvalidateData(); } } private void addAppsWithoutInvalidate(ArrayList<AppInfo> list) { // We add it in place, in alphabetical order int count = list.size(); for (int i = 0; i < count; ++i) { AppInfo info = list.get(i); int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator()); if (index < 0) { mApps.add(-(index + 1), info); } } } public void addApps(ArrayList<AppInfo> list) { if (!LauncherAppState.isDisableAllApps()) { addAppsWithoutInvalidate(list); updatePageCountsAndInvalidateData(); } } private int findAppByComponent(List<ItemInfo> list, AppInfo item) { ComponentName removeComponent = item.intent.getComponent(); int length = list.size(); for (int i = 0; i < length; ++i) { ItemInfo itemInfo = list.get(i); if (itemInfo instanceof AppInfo) { AppInfo info = (AppInfo) itemInfo; if (info.user.equals(item.user) && info.intent.getComponent().equals(removeComponent)) { return i; } } } return -1; } private void removeAppsWithoutInvalidate(ArrayList<AppInfo> list) { // loop through all the apps and remove apps that have the same component int length = list.size(); for (int i = 0; i < length; ++i) { AppInfo info = list.get(i); int removeIndex = findAppByComponent(mApps, info); if (removeIndex > -1) { mApps.remove(removeIndex); } } } public void removeApps(ArrayList<AppInfo> appInfos) { if (!LauncherAppState.isDisableAllApps()) { removeAppsWithoutInvalidate(appInfos); updatePageCountsAndInvalidateData(); } } public void updateApps(ArrayList<AppInfo> list) { // We remove and re-add the updated applications list because it's properties may have // changed (ie. the title), and this will ensure that the items will be in their proper // place in the list. if (!LauncherAppState.isDisableAllApps()) { removeAppsWithoutInvalidate(list); addAppsWithoutInvalidate(list); updatePageCountsAndInvalidateData(); } } public void reset() { // If we have reset, then we should not continue to restore the previous state mSaveInstanceStateItemIndex = -1; if (mContentType != ContentType.Applications) { setContentType(ContentType.Applications); } 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. // AppInfo.dumpApplicationInfoList(TAG, "mApps", mApps); // dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets); } private void dumpAppWidgetProviderInfoList(String tag, String label, ArrayList<Object> 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(); } /* * 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.Applications) { stringId = R.string.apps_customize_apps_scroll_format; count = mNumAppsPages; } else 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); } //DropTarget private Bitmap mDragOutline = null; private final int[] mTempXY = new int[2]; private ShortcutAndWidgetContainer mDragSourceInternal; private boolean mCreateUserFolderOnDrop = false; private boolean mAddToExistingFolderOnDrop = false; private float[] mDragViewVisualCenter = new float[2]; private Matrix mTempInverseMatrix = new Matrix(); private float[] mTempCellLayoutCenterCoordinates = new float[2]; private final DragUtils mDragUtils; /** * The CellLayout that is currently being dragged over */ private CellLayout mDragTargetLayout = null; /** * The CellLayout that we will show as glowing */ private CellLayout mDragOverlappingLayout = null; /** * The CellLayout which will be dropped to */ private CellLayout mDropToLayout = null; /** * Target drop area calculated during last acceptDrop call. */ private int[] mTargetCell = new int[2]; private int mDragOverX = -1; private int mDragOverY = -1; /** * CellInfo for the cell that is currently being dragged */ private CellLayout.CellInfo mDragInfo; private State mState = State.NORMAL; private DropTarget.DragEnforcer mDragEnforcer; /** Is the user is dragging an item near the edge of a page? */ private boolean mInScrollArea = false; private HashMap<Long, AppsCustomizeCellLayout> mAllAppsScreens = new HashMap<Long, AppsCustomizeCellLayout>(); private ArrayList<Long> mScreenOrder = new ArrayList<Long>(); @Override public boolean isDropEnabled() { return true; } /** * ??????? * ????? * ?????? * ???? * ?dragView * @param dragObject */ @Override public void onDrop(final DragObject dragObject) { System.out.println("AppsCustomizePagedView onDrop"); mDragViewVisualCenter = mDragUtils.getDragViewVisualCenter(dragObject.x, dragObject.y, dragObject.xOffset, dragObject.yOffset, dragObject.dragView, mDragViewVisualCenter, getResources()); CellLayout dropTargetLayout = mDropToLayout; // We want the point to be mapped to the dragTarget. if (dropTargetLayout != null) { if (mLauncher.isHotseatLayout(dropTargetLayout)) { mDragUtils.mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { mDragUtils.mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); } } int snapScreen = -1; boolean resizeOnDrop = false; if (dragObject.dragSource != this) { final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1] }; // onDropExternal(touchXY, dragObject.dragInfo, dropTargetLayout, false, dragObject); } else if (mDragInfo != null) { final View cell = mDragInfo.cell; Runnable resizeRunnable = null; if (dropTargetLayout != null && !dragObject.cancelled) { // Move internally boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout); boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout); long container = hasMovedIntoHotseat ? LauncherSettings.Favorites.CONTAINER_HOTSEAT : LauncherSettings.Allapps.CONTAINER_ALLAPPS; long screenId = (mTargetCell[0] < 0) ? mDragInfo.screenId : getIdForScreen(dropTargetLayout); int spanX = mDragInfo != null ? mDragInfo.spanX : 1; int spanY = mDragInfo != null ? mDragInfo.spanY : 1; // First we find the cell nearest to point at which the item is // dropped, without any consideration to whether there is an item there. mTargetCell = mDragUtils.findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell); float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); System.out.println("onDrop ?"); // If the item being dropped is a shortcut and the nearest drop // cell also contains a shortcut, then create a folder with the two shortcuts. if (!mInScrollArea && createUserFolderIfNecessary(cell, container, dropTargetLayout, mTargetCell, distance, false, dragObject.dragView, null)) { return; } System.out.println("onDrop ?"); if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, dragObject, false)) { return; } System.out.println("onDrop ?"); // Aside from the special case where we're dropping a shortcut onto a shortcut, // we need to find the nearest cell location that is vacant ItemInfo item = (ItemInfo) dragObject.dragInfo; int minSpanX = item.spanX; int minSpanY = item.spanY; if (item.minSpanX > 0 && item.minSpanY > 0) { minSpanX = item.minSpanX; minSpanY = item.minSpanY; } int[] resultSpan = new int[2]; mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP); boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0; // if the widget resizes on drop if (foundCell && (cell instanceof AppWidgetHostView) && (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) { resizeOnDrop = true; item.spanX = resultSpan[0]; item.spanY = resultSpan[1]; AppWidgetHostView awhv = (AppWidgetHostView) cell; AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0], resultSpan[1]); } if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) { snapScreen = getPageIndexForScreenId(screenId); snapToPage(snapScreen); } System.out.println("onDrop foundCell:" + foundCell); if (foundCell) { final ItemInfo info = (ItemInfo) cell.getTag(); if (hasMovedLayouts) { // Reparent the view CellLayout parentCell = getParentCellLayoutForView(cell); if (parentCell != null) { parentCell.removeView(cell); } else if (LauncherAppState.isDogfoodBuild()) { throw new NullPointerException("mDragInfo.cell has null parent"); } addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX, info.spanY); } // update the item's position after drop CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); lp.cellX = lp.tmpCellX = mTargetCell[0]; lp.cellY = lp.tmpCellY = mTargetCell[1]; lp.cellHSpan = item.spanX; lp.cellVSpan = item.spanY; lp.isLockedToGrid = true; if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT && cell instanceof LauncherAppWidgetHostView) { final CellLayout cellLayout = dropTargetLayout; // We post this call so that the widget has a chance to be placed // in its final location final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo(); if (pinfo != null && pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) { final Runnable addResizeFrame = new Runnable() { public void run() { DragLayer dragLayer = mLauncher.getDragLayer(); dragLayer.addResizeFrame(info, hostView, cellLayout); } }; resizeRunnable = (new Runnable() { public void run() { if (!isPageMoving()) { addResizeFrame.run(); } else { mDelayedResizeRunnable = addResizeFrame; } } }); } } System.out.println("onDrop ?"); LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX, lp.cellY, item.spanX, item.spanY); } else { // If we can't find a drop location, we return the item to its original position CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); mTargetCell[0] = lp.cellX; mTargetCell[1] = lp.cellY; CellLayout layout = (CellLayout) cell.getParent().getParent(); layout.markCellsAsOccupiedForView(cell); } } final CellLayout parent = (CellLayout) cell.getParent().getParent(); final Runnable finalResizeRunnable = resizeRunnable; // Prepare it to be animated into its new position // This must be called after the view has been re-parented final Runnable onCompleteRunnable = new Runnable() { @Override public void run() { mAnimatingViewIntoPlace = false; updateChildrenLayersEnabled(false); if (finalResizeRunnable != null) { finalResizeRunnable.run(); } } }; mAnimatingViewIntoPlace = true; if (dragObject.dragView.hasDrawn()) { final ItemInfo info = (ItemInfo) cell.getTag(); if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) { int animationType = resizeOnDrop ? Workspace.ANIMATE_INTO_POSITION_AND_RESIZE : Workspace.ANIMATE_INTO_POSITION_AND_DISAPPEAR; animateWidgetDrop(info, parent, dragObject.dragView, onCompleteRunnable, animationType, cell, false); } else { int duration = snapScreen < 0 ? -1 : 300; mLauncher.getDragLayer().animateViewIntoPosition(dragObject.dragView, cell, duration, onCompleteRunnable, this); } } else { dragObject.deferDragViewCleanupPostAnimation = false; cell.setVisibility(VISIBLE); } parent.onDropChild(cell); } } @Override public void onDragEnter(DragObject dragObject) { mDragEnforcer.onDragEnter(); mCreateUserFolderOnDrop = false; mAddToExistingFolderOnDrop = false; mDropToLayout = null; CellLayout layout = getCurrentDropLayout(); setCurrentDropLayout(layout); setCurrentDragOverlappingLayout(layout); if (!workspaceInModalState()) { mLauncher.getDragLayer().showPageHints(); } } @Override public void onDragOver(DragObject d) { if (mInScrollArea || !transitionStateShouldAllowDrop()) return; Rect r = new Rect(); CellLayout layout = null; ItemInfo item = (ItemInfo) d.dragInfo; if (item == null) { if (LauncherAppState.isDogfoodBuild()) { throw new NullPointerException("DragObject has null info"); } return; } // Ensure that we have proper spans for the item that we are dropping if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found"); mDragViewVisualCenter = mDragUtils.getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, mDragViewVisualCenter, getResources()); final View child = (mDragInfo == null) ? null : mDragInfo.cell; // Identify whether we have dragged over a side page if (workspaceInModalState()) { if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) { if (mDragUtils.isPointInSelfOverHotseat(d.x, d.y, r)) { layout = mLauncher.getHotseat().getLayout(); } } if (layout == null) { layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false); } if (layout != mDragTargetLayout) { setCurrentDropLayout(layout); setCurrentDragOverlappingLayout(layout); //?? // boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED); // if (isInSpringLoadedMode) { // if (mLauncher.isHotseatLayout(layout)) { // mSpringLoadedDragController.cancel(); // } else { // mSpringLoadedDragController.setAlarm(mDragTargetLayout); // } // } } } else { // Test to see if we are over the hotseat otherwise just use the current page if (mLauncher.getHotseat() != null && !isDragWidget(d)) { if (mDragUtils.isPointInSelfOverHotseat(d.x, d.y, r)) { layout = mLauncher.getHotseat().getLayout(); } } if (layout == null) { layout = getCurrentDropLayout(); } if (layout != mDragTargetLayout) { setCurrentDropLayout(layout); setCurrentDragOverlappingLayout(layout); } } // Handle the drag over if (mDragTargetLayout != null) { // We want the point to be mapped to the dragTarget. if (mLauncher.isHotseatLayout(mDragTargetLayout)) { mDragUtils.mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { mDragUtils.mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null); } ItemInfo info = (ItemInfo) d.dragInfo; int minSpanX = item.spanX; int minSpanY = item.spanY; if (item.minSpanX > 0 && item.minSpanY > 0) { minSpanX = item.minSpanX; minSpanY = item.minSpanY; } mTargetCell = mDragUtils.findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout, mTargetCell); int reorderX = mTargetCell[0]; int reorderY = mTargetCell[1]; setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]); float targetCellDistance = mDragTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]); manageFolderFeedback(info, mDragTargetLayout, mTargetCell, targetCellDistance, dragOverView); boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied( (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX, item.spanY, child, mTargetCell); if (!nearestDropOccupied) { mDragTargetLayout.visualizeDropLocation(child, mDragOutline, (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion()); } else if ((mDragUtils.mDragMode == DragUtils.DRAG_MODE_NONE || mDragUtils.mDragMode == DragUtils.DRAG_MODE_REORDER) && !mDragUtils.mReorderAlarm.alarmPending() && (mDragUtils.mLastReorderX != reorderX || mDragUtils.mLastReorderY != reorderY)) { int[] resultSpan = new int[2]; mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY, child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT); // Otherwise, if we aren't adding to or creating a folder and there's no pending // reorder, then we schedule a reorder ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter, minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child); mDragUtils.mReorderAlarm.setOnAlarmListener(listener); mDragUtils.mReorderAlarm.setAlarm(Workspace.REORDER_TIMEOUT); } if (mDragUtils.mDragMode == DragUtils.DRAG_MODE_CREATE_FOLDER || mDragUtils.mDragMode == DragUtils.DRAG_MODE_ADD_TO_FOLDER || !nearestDropOccupied) { if (mDragTargetLayout != null) { mDragTargetLayout.revertTempState(); } } } } @Override public void onDragExit(DragObject dragObject) { mDragEnforcer.onDragExit(); // Here we store the final page that will be dropped to, if the workspace in fact // receives the drop if (mInScrollArea) { if (isPageMoving()) { // If the user drops while the page is scrolling, we should use that page as the // destination instead of the page that is being hovered over. mDropToLayout = (CellLayout) getPageAt(getNextPage()); } else { mDropToLayout = mDragOverlappingLayout; } } else { mDropToLayout = mDragTargetLayout; } if (mDragUtils.mDragMode == DragUtils.DRAG_MODE_CREATE_FOLDER) { mCreateUserFolderOnDrop = true; } else if (mDragUtils.mDragMode == DragUtils.DRAG_MODE_ADD_TO_FOLDER) { mAddToExistingFolderOnDrop = true; } // Reset the scroll area and previous drag target onResetScrollArea(); setCurrentDropLayout(null); setCurrentDragOverlappingLayout(null); // mSpringLoadedDragController.cancel(); if (!mIsPageMoving) { //FIXME System.out.println("AppsCustomizePagedView !mIsPageMoving is true hideOutlines"); // hideOutlines(); } mLauncher.getDragLayer().hidePageHints(); } @Override public void onFlingToDelete(DragObject dragObject, int x, int y, PointF vec) { System.out.println("AppsCustomizePagedView onFlingToDelete"); } @Override public boolean acceptDrop(DragObject dragObject) { // If it's an external drop (e.g. from All Apps), check if it should be accepted CellLayout dropTargetLayout = mDropToLayout; if (dragObject.dragSource != this) { // Don't accept the drop if we're not over a screen at time of drop if (dropTargetLayout == null) { return false; } if (!transitionStateShouldAllowDrop()) return false; mDragViewVisualCenter = mDragUtils.getDragViewVisualCenter(dragObject.x, dragObject.y, dragObject.xOffset, dragObject.yOffset, dragObject.dragView, mDragViewVisualCenter, getResources()); // We want the point to be mapped to the dragTarget. if (mLauncher.isHotseatLayout(dropTargetLayout)) { mDragUtils.mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter); } else { mDragUtils.mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null); } int spanX = 1; int spanY = 1; if (mDragInfo != null) { final CellLayout.CellInfo dragCellInfo = mDragInfo; spanX = dragCellInfo.spanX; spanY = dragCellInfo.spanY; } else { final ItemInfo dragInfo = (ItemInfo) dragObject.dragInfo; spanX = dragInfo.spanX; spanY = dragInfo.spanY; } int minSpanX = spanX; int minSpanY = spanY; if (dragObject.dragInfo instanceof PendingAddWidgetInfo) { minSpanX = ((PendingAddWidgetInfo) dragObject.dragInfo).minSpanX; minSpanY = ((PendingAddWidgetInfo) dragObject.dragInfo).minSpanY; } mTargetCell = mDragUtils.findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout, mTargetCell); float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell); if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) dragObject.dragInfo, dropTargetLayout, mTargetCell, distance, true)) { return true; } if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) dragObject.dragInfo, dropTargetLayout, mTargetCell, distance)) { return true; } int[] resultSpan = new int[2]; mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP); boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0; // Don't accept the drop if there's no room for the item if (!foundCell) { // Don't show the message if we are dropping on the AllApps button and the hotseat // is full boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout); if (mTargetCell != null && isHotseat) { Hotseat hotseat = mLauncher.getHotseat(); if (hotseat.isAllAppsButtonRank(hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) { return false; } } mLauncher.showOutOfSpaceMessage(isHotseat); return false; } } //FIXME AllApps???? // long screenId = getIdForScreen(dropTargetLayout); // if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) { // commitExtraEmptyScreen(); // } return true; } @Override public void getHitRectRelativeToDragLayer(Rect outRect) { // We want the workspace to have the whole area of the display (it will find the correct // cell layout to drop to in the existing drag/drop logic. //FIXME //thisoutRect??? mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getWorkspace(), outRect); } @Override public void getLocationInDragLayer(int[] loc) { mLauncher.getDragLayer().getLocationInDragLayer(this, loc); } //DropTarget? //DragController.DragListener @Override public void onDragStart(DragSource source, Object info, int dragAction) { mDragUtils.mIsDragOccuring = true; // updateChildrenLayersEnabled(false); mLauncher.lockScreenOrientation(); mLauncher.onInteractionBegin(); setChildrenBackgroundAlphaMultipliers(1f); // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging InstallShortcutReceiver.enableInstallQueue(); UninstallShortcutReceiver.enableUninstallQueue(); post(new Runnable() { @Override public void run() { if (mDragUtils.mIsDragOccuring) { //FIXME ? // mDeferRemoveExtraEmptyScreen = false; // addExtraEmptyScreenOnDrag(); } } }); } @Override public void onDragEnd() { //FIXME ? // if (!mDeferRemoveExtraEmptyScreen) { // removeExtraEmptyScreen(true, mDragSourceInternal != null); // } mDragUtils.mIsDragOccuring = false; updateChildrenLayersEnabled(false); mLauncher.unlockScreenOrientation(false); // Re-enable any Un/InstallShortcutReceiver and now process any queued items InstallShortcutReceiver.disableAndFlushInstallQueue(getContext()); UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext()); mDragSourceInternal = null; mLauncher.onInteractionEnd(); mLauncher.getAllAppsButton().setIsEnable(false); } //DragScroller @Override public boolean onEnterScrollArea(int x, int y, int direction) { // Ignore the scroll area if we are dragging over the hot seat boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext()); if (mLauncher.getHotseat() != null && isPortrait) { Rect r = new Rect(); mLauncher.getHotseat().getHitRect(r); if (r.contains(x, y)) { return false; } } boolean result = false; // if (!workspaceInModalState() && !mIsSwitchingState && mDragUtils.getOpenFolder() == null) { if (!workspaceInModalState() && mDragUtils.getOpenFolder() == null) { mInScrollArea = true; final int page = getNextPage() + (direction == DragController.SCROLL_LEFT ? -1 : 1); // We always want to exit the current layout to ensure parity of enter / exit setCurrentDropLayout(null); if (0 <= page && page < getChildCount()) { // Ensure that we are not dragging over to the custom content screen //FIXME // if (getScreenIdForPageIndex(page) == Workspace.CUSTOM_CONTENT_SCREEN_ID) { // return false; // } CellLayout layout = (CellLayout) getChildAt(page); setCurrentDragOverlappingLayout(layout); // Workspace is responsible for drawing the edge glow on adjacent pages, // so we need to redraw the workspace when this may have changed. invalidate(); result = true; } } return result; } @Override public boolean onExitScrollArea() { boolean result = false; if (mInScrollArea) { invalidate(); CellLayout layout = getCurrentDropLayout(); setCurrentDropLayout(layout); setCurrentDragOverlappingLayout(layout); result = true; mInScrollArea = false; } return result; } void setCurrentDropLayout(CellLayout layout) { if (mDragTargetLayout != null) { mDragTargetLayout.revertTempState(); mDragTargetLayout.onDragExit(); } mDragTargetLayout = layout; if (mDragTargetLayout != null) { mDragTargetLayout.onDragEnter(); } mDragUtils.cleanupReorder(true); mDragUtils.cleanupFolderCreation(); setCurrentDropOverCell(-1, -1); } void startDrag(CellLayout.CellInfo cellInfo) { View child = cellInfo.cell; // Make sure the drag was started by a long press as opposed to a long click. if (!child.isInTouchMode()) { return; } mDragInfo = cellInfo; child.setVisibility(INVISIBLE); CellLayout layout = (CellLayout) child.getParent().getParent(); layout.prepareChildForDrag(child); mLauncher.setDragController(false); beginDragShared(child, this); } public void beginDragShared(View child, DragSource source) { // child.clearFocus(); //??false?false? child.setPressed(false); //??????? // The outline is used to visualize where the item will land if dropped mDragOutline = createDragOutline(child, Workspace.DRAG_BITMAP_PADDING); // mLauncher.onDragStarted(child); // The drag bitmap follows the touch point around on the screen AtomicInteger padding = new AtomicInteger(Workspace.DRAG_BITMAP_PADDING); final Bitmap b = Utils.createDragBitmap(child, padding); final int bmpWidth = b.getWidth(); final int bmpHeight = b.getHeight(); float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY); int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2); int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 - padding.get() / 2); LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { int iconSize = grid.iconSizePx; int top = child.getPaddingTop(); int left = (bmpWidth - iconSize) / 2; int right = left + iconSize; int bottom = top + iconSize; dragLayerY += top; // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2); dragRect = new Rect(left, top, right, bottom); } else if (child instanceof FolderIcon) { int previewSize = grid.folderIconSizePx; dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize); } // Clear the pressed state if necessary if (child instanceof BubbleTextView) { BubbleTextView icon = (BubbleTextView) child; icon.clearPressedBackground(); } if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) { String msg = "Drag started with a view that has no tag set. This " + "will cause a crash (issue 11627249) down the line. " + "View: " + child + " tag: " + child.getTag(); throw new IllegalStateException(msg); } DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); if (child.getParent() instanceof ShortcutAndWidgetContainer) { mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } b.recycle(); } // private HolographicOutlineHelper mOutlineHelper; /** * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. * Responsibility for the bitmap is transferred to the caller. */ private Bitmap createDragOutline(View v, int padding) { final int outlineColor = getResources().getColor(R.color.outline_color); final Bitmap b = Bitmap.createBitmap(v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888); Canvas mCanvas = new Canvas(); mCanvas.setBitmap(b); Utils.drawDragView(v, mCanvas, padding); // mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor); mCanvas.setBitmap(null); return b; } void setCurrentDragOverlappingLayout(CellLayout layout) { if (mDragOverlappingLayout != null) { mDragOverlappingLayout.setIsDragOverlapping(false); } mDragOverlappingLayout = layout; if (mDragOverlappingLayout != null) { mDragOverlappingLayout.setIsDragOverlapping(true); } invalidate(); } /* * * This method returns the CellLayout that is currently being dragged to. In order to drag * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one * * Return null if no CellLayout is currently being dragged over * */ private CellLayout findMatchingPageForDragOver(DragView dragView, float originX, float originY, boolean exact) { // We loop through all the screens (ie CellLayouts) and see which ones overlap // with the item being dragged and then choose the one that's closest to the touch point final int screenCount = getChildCount(); CellLayout bestMatchingScreen = null; float smallestDistSoFar = Float.MAX_VALUE; for (int i = 0; i < screenCount; i++) { // The custom content screen is not a valid drag over option // if (mScreenOrder.get(i) == Workspace.CUSTOM_CONTENT_SCREEN_ID) { // continue; // } CellLayout cl = (CellLayout) getChildAt(i); final float[] touchXy = { originX, originY }; // Transform the touch coordinates to the CellLayout's local coordinates // If the touch point is within the bounds of the cell layout, we can return immediately cl.getMatrix().invert(mTempInverseMatrix); mDragUtils.mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix); if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() && touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) { return cl; } if (!exact) { // Get the center of the cell layout in screen coordinates final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates; cellLayoutCenter[0] = cl.getWidth() / 2; cellLayoutCenter[1] = cl.getHeight() / 2; mDragUtils.mapPointFromChildToSelf(cl, cellLayoutCenter); touchXy[0] = originX; touchXy[1] = originY; // Calculate the distance between the center of the CellLayout // and the touch point float dist = mDragUtils.squaredDistance(touchXy, cellLayoutCenter); if (dist < smallestDistSoFar) { smallestDistSoFar = dist; bestMatchingScreen = cl; } } } return bestMatchingScreen; } /** * Return the current {@link CellLayout}, correctly picking the destination * screen while a scroll is in progress. */ public CellLayout getCurrentDropLayout() { return (CellLayout) getChildAt(getNextPage()); } private boolean isDragWidget(DragObject d) { return (d.dragInfo instanceof LauncherAppWidgetInfo || d.dragInfo instanceof PendingAddWidgetInfo); } private boolean isExternalDragWidget(DragObject d) { return d.dragSource != this && isDragWidget(d); } private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout, int[] targetCell, float distance, View dragOverView) { boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance, false); if (mDragUtils.mDragMode == DragUtils.DRAG_MODE_NONE && userFolderPending && !mDragUtils.mFolderCreationAlarm.alarmPending()) { mDragUtils.mFolderCreationAlarm .setOnAlarmListener(mDragUtils.getNewFCAL(targetLayout, targetCell[0], targetCell[1])); mDragUtils.mFolderCreationAlarm.setAlarm(Workspace.FOLDER_CREATION_TIMEOUT); return; } boolean willAddToFolder = willAddToExistingUserFolder(info, targetLayout, targetCell, distance); if (willAddToFolder && mDragUtils.mDragMode == DragUtils.DRAG_MODE_NONE) { mDragUtils.mDragOverFolderIcon = ((FolderIcon) dragOverView); mDragUtils.mDragOverFolderIcon.onDragEnter(info); if (targetLayout != null) { targetLayout.clearDragOutlines(); } mDragUtils.setDragMode(DragUtils.DRAG_MODE_ADD_TO_FOLDER); return; } if (mDragUtils.mDragMode == DragUtils.DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) { mDragUtils.setDragMode(DragUtils.DRAG_MODE_NONE); } if (mDragUtils.mDragMode == DragUtils.DRAG_MODE_CREATE_FOLDER && !userFolderPending) { mDragUtils.setDragMode(DragUtils.DRAG_MODE_NONE); } return; } boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float distance, boolean considerTimeout) { if (distance > mMaxDistanceForFolderCreation) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); if (dropOverView != null) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { return false; } } boolean hasntMoved = false; if (mDragInfo != null) { hasntMoved = dropOverView == mDragInfo.cell; } if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) { return false; } Object tag = dropOverView.getTag(); boolean aboveShortcut = (tag instanceof ShortcutInfo); boolean willBecomeShortcut = (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT); boolean aboveApplication = (tag instanceof AppInfo); return ((aboveShortcut || aboveApplication) && willBecomeShortcut); } boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell, float distance) { if (distance > mMaxDistanceForFolderCreation) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); if (dropOverView != null) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams(); if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) { return false; } } if (dropOverView instanceof FolderIcon) { FolderIcon fi = (FolderIcon) dropOverView; if (fi.acceptDrop(dragInfo)) { return true; } } return false; } void setCurrentDropOverCell(int x, int y) { if (x != mDragOverX || y != mDragOverY) { mDragOverX = x; mDragOverY = y; mDragUtils.setDragMode(DragUtils.DRAG_MODE_NONE); } } public boolean workspaceInModalState() { return mState != State.NORMAL; } private void setState(State state) { mState = state; updateInteractionForState(); updateAccessibilityFlags(); } public void updateInteractionForState() { if (mState != State.NORMAL) { mLauncher.onInteractionBegin(); } else { mLauncher.onInteractionEnd(); } } private void updateAccessibilityFlags() { int accessible = mState == State.NORMAL ? ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES : ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; setImportantForAccessibility(accessible); } private boolean transitionStateShouldAllowDrop() { //isSwitchingState() ????Menu // return ((!isSwitchingState() || mTransitionProgress > 0.5f) && // (mState == State.NORMAL || mState == State.SPRING_LOADED)); return ((mTransitionProgress > 0.5f) && (mState == State.NORMAL || mState == State.SPRING_LOADED)); } private void onResetScrollArea() { setCurrentDragOverlappingLayout(null); mInScrollArea = false; } private void setChildrenBackgroundAlphaMultipliers(float a) { for (int i = 0; i < getChildCount(); i++) { CellLayout child = (CellLayout) getChildAt(i); child.setBackgroundAlphaMultiplier(a); } } /** * Returns a specific CellLayout */ public CellLayout getParentCellLayoutForView(View v) { ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts(); for (CellLayout layout : layouts) { if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) { return layout; } } return null; } /** * Returns a list of all the CellLayouts in the workspace. */ private ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() { ArrayList<CellLayout> layouts = new ArrayList<CellLayout>(); int screenCount = getChildCount(); for (int screen = 0; screen < screenCount; screen++) { layouts.add(((CellLayout) getChildAt(screen))); } if (mLauncher.getHotseat() != null) { layouts.add(mLauncher.getHotseat().getLayout()); } return layouts; } public long getIdForScreen(CellLayout layout) { Iterator<Long> iter = mAllAppsScreens.keySet().iterator(); while (iter.hasNext()) { long id = iter.next(); if (mAllAppsScreens.get(id) == layout) { return id; } } return -1; } boolean createUserFolderIfNecessary(View newView, long container, CellLayout target, int[] targetCell, float distance, boolean external, DragView dragView, Runnable postAnimationRunnable) { if (distance > mMaxDistanceForFolderCreation) return false; View v = target.getChildAt(targetCell[0], targetCell[1]); boolean hasntMoved = false; if (mDragInfo != null) { CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell); hasntMoved = (mDragInfo.cellX == targetCell[0] && mDragInfo.cellY == targetCell[1]) && (cellParent == target); } if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false; mCreateUserFolderOnDrop = false; final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target); boolean aboveShortcut = (v.getTag() instanceof AppInfo); boolean willBecomeShortcut = (newView.getTag() instanceof AppInfo); if (aboveShortcut && willBecomeShortcut) { AppInfo sourceInfo = (AppInfo) newView.getTag(); AppInfo destInfo = (AppInfo) v.getTag(); // if the drag started here, we need to remove it from the workspace if (!external) { getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell); } Rect folderLocation = new Rect(); float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation); target.removeView(v); FolderIcon fi = mLauncher.addAllAppFolder(target, container, screenId, targetCell[0], targetCell[1], sourceInfo.rank); destInfo.cellX = -1; destInfo.cellY = -1; sourceInfo.cellX = -1; sourceInfo.cellY = -1; // If the dragView is null, we can't animate boolean animate = dragView != null; if (animate) { fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale, postAnimationRunnable); } else { fi.addItem(destInfo); fi.addItem(sourceInfo); } return true; } return false; } boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell, float distance, DragObject d, boolean external) { if (distance > mMaxDistanceForFolderCreation) return false; View dropOverView = target.getChildAt(targetCell[0], targetCell[1]); if (!mAddToExistingFolderOnDrop) return false; mAddToExistingFolderOnDrop = false; if (dropOverView instanceof FolderIcon) { FolderIcon fi = (FolderIcon) dropOverView; if (fi.acceptDrop(d.dragInfo)) { fi.onDrop(d); // if the drag started here, we need to remove it from the workspace if (!external) { getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell); } return true; } } return false; } public long getScreenIdForPageIndex(int index) { if (0 <= index && index < mScreenOrder.size()) { return mScreenOrder.get(index); } return -1; } public int getPageIndexForScreenId(long screenId) { return indexOfChild(mAllAppsScreens.get(screenId)); } // See implementation for parameter definition. void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY) { addInScreen(child, container, screenId, x, y, spanX, spanY, false, false); } // At bind time, we use the rank (screenId) to compute x and y for hotseat items. // See implementation for parameter definition. void addInScreenFromBind(View child, long container, long screenId, int x, int y, int spanX, int spanY) { addInScreen(child, container, screenId, x, y, spanX, spanY, false, true); } // See implementation for parameter definition. void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY, boolean insert) { addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false); } /** * Adds the specified child in the specified screen. The position and dimension of * the child are defined by x, y, spanX and spanY. * * @param child The child to add in one of the workspace's screens. * @param screenId The screen in which to add the child. * @param x The X position of the child in the screen's grid. * @param y The Y position of the child in the screen's grid. * @param spanX The number of cells spanned horizontally by the child. * @param spanY The number of cells spanned vertically by the child. * @param insert When true, the child is inserted at the beginning of the children list. * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute * the x and y position in which to place hotseat items. Otherwise * we use the x and y position to compute the rank. */ void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY, boolean insert, boolean computeXYFromRank) { if (container == LauncherSettings.Allapps.CONTAINER_ALLAPPS) { if (getScreenWithId(screenId) == null) { Log.e(TAG, "Skipping child, screenId " + screenId + " not found"); // DEBUGGING - Print out the stack trace to see where we are adding from new Throwable().printStackTrace(); return; } } if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) { // This should never happen throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID"); } final CellLayout layout; if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { layout = mLauncher.getHotseat().getLayout(); child.setOnKeyListener(new HotseatIconKeyEventListener()); // Hide folder title in the hotseat if (child instanceof FolderIcon) { ((FolderIcon) child).setTextVisible(false); } if (computeXYFromRank) { x = mLauncher.getHotseat().getCellXFromOrder((int) screenId); y = mLauncher.getHotseat().getCellYFromOrder((int) screenId); } else { screenId = mLauncher.getHotseat().getOrderInHotseat(x, y); } } else { // Show folder title if not in the hotseat if (child instanceof FolderIcon) { ((FolderIcon) child).setTextVisible(true); } layout = getScreenWithId(screenId); child.setOnKeyListener(new IconKeyEventListener()); } ViewGroup.LayoutParams genericLp = child.getLayoutParams(); CellLayout.LayoutParams lp; if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) { lp = new CellLayout.LayoutParams(x, y, spanX, spanY); } else { lp = (CellLayout.LayoutParams) genericLp; lp.cellX = x; lp.cellY = y; lp.cellHSpan = spanX; lp.cellVSpan = spanY; } if (spanX < 0 && spanY < 0) { lp.isLockedToGrid = false; } // Get the canonical child id to uniquely represent this view in this screen ItemInfo info = (ItemInfo) child.getTag(); int childId = mLauncher.getAllAppsViewIdForItem(info); boolean markCellsAsOccupied = !(child instanceof Folder); if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) { // TODO: This branch occurs when the workspace is adding views // outside of the defined grid // maybe we should be deleting these items from the LauncherModel? System.out.println("Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true); } if (!(child instanceof Folder)) { child.setHapticFeedbackEnabled(false); child.setOnLongClickListener(mLongClickListener); } if (child instanceof DropTarget) { mDragController.addDropTarget((DropTarget) child); } } private void updateChildrenLayersEnabled(boolean force) { //FIXME boolean small = mState == State.OVERVIEW;//|| mIsSwitchingState; boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving(); if (enableChildrenLayers != mChildrenLayersEnabled) { mChildrenLayersEnabled = enableChildrenLayers; if (mChildrenLayersEnabled) { enableHwLayersOnVisiblePages(); } else { for (int i = 0; i < getPageCount(); i++) { final CellLayout cl = (CellLayout) getChildAt(i); cl.enableHardwareLayer(false); } } } } public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView, final Runnable onCompleteRunnable, int animationType, final View finalView, boolean external) { System.out.println("appsCustomizePagedView animateWidgetDrop"); // Rect from = new Rect(); // mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from); // // int[] finalPos = new int[2]; // float scaleXY[] = new float[2]; // boolean scalePreview = !(info instanceof PendingAddShortcutInfo); // getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell, // external, scalePreview); // // Resources res = mLauncher.getResources(); // final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; // // // In the case where we've prebound the widget, we remove it from the DragLayer // if (finalView instanceof AppWidgetHostView && external) { // Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView"); // mLauncher.getDragLayer().removeView(finalView); // } // if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) { // Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView); // dragView.setCrossFadeBitmap(crossFadeBitmap); // dragView.crossFade((int) (duration * 0.8f)); // } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) { // scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]); // } // // DragLayer dragLayer = mLauncher.getDragLayer(); // if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) { // mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f, // DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration); // } else { // int endStyle; // if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) { // endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE; // } else { // endStyle = DragLayer.ANIMATION_END_DISAPPEAR;; // } // // Runnable onComplete = new Runnable() { // @Override // public void run() { // if (finalView != null) { // finalView.setVisibility(VISIBLE); // } // if (onCompleteRunnable != null) { // onCompleteRunnable.run(); // } // } // }; // dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0], // finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle, // duration, this); // } } public void addExtraEmptyScreenOnDrag() { // Log to disk Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreenOnDrag()", true); boolean lastChildOnScreen = false; boolean childOnFinalScreen = false; // Cancel any pending removal of empty screen // mRemoveEmptyScreenRunnable = null; if (mDragSourceInternal != null) { if (mDragSourceInternal.getChildCount() == 1) { lastChildOnScreen = true; } CellLayout cl = (CellLayout) mDragSourceInternal.getParent(); if (indexOfChild(cl) == getChildCount() - 1) { childOnFinalScreen = true; } } // If this is the last item on the final screen if (lastChildOnScreen && childOnFinalScreen) { return; } if (!mAllAppsScreens.containsKey(Workspace.EXTRA_EMPTY_SCREEN_ID)) { insertNewAllAppsScreen(Workspace.EXTRA_EMPTY_SCREEN_ID); } } public boolean addExtraEmptyScreen() { // Log to disk Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreen()", true); if (!mAllAppsScreens.containsKey(Workspace.EXTRA_EMPTY_SCREEN_ID)) { insertNewAllAppsScreen(Workspace.EXTRA_EMPTY_SCREEN_ID); return true; } return false; } public long insertNewAllAppsScreenBeforeEmptyScreen(long screenId) { // Find the index to insert this view into. If the empty screen exists, then // insert it before that. int insertIndex = mScreenOrder.indexOf(Workspace.EXTRA_EMPTY_SCREEN_ID); if (insertIndex < 0) { insertIndex = mScreenOrder.size(); } return insertNewAllAppsScreen(screenId, insertIndex); } public long insertNewAllAppsScreen(long screenId) { return insertNewAllAppsScreen(screenId, getChildCount()); } public long insertNewAllAppsScreen(long screenId, int insertIndex) { // Log to disk Launcher.addDumpLog(TAG, "11683562 - insertNewWorkspaceScreen(): " + screenId + " at index: " + insertIndex, true); if (mAllAppsScreens.containsKey(screenId)) { throw new RuntimeException("Screen id " + screenId + " already exists!"); } AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(getContext()); setupPage(layout); mAllAppsScreens.put(screenId, layout); mScreenOrder.add(insertIndex, screenId); addView(layout, insertIndex, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); return screenId; } // At bind time, we use the rank (screenId) to compute x and y for hotseat items. // See implementation for parameter definition. void addInScreenFromBind(View child, long screenId, int x, int y, ItemInfo itemInfo, AppsCustomizeCellLayout layout) { if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) { // This should never happen throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID"); } int childId = mLauncher.getViewIdForItem(itemInfo); boolean markCellsAsOccupied = !(child instanceof Folder); layout.addViewToCellLayout(child, -1, childId, new CellLayout.LayoutParams(x, y, 1, 1), markCellsAsOccupied); } public AppsCustomizeCellLayout getScreenWithId(long screenId) { AppsCustomizeCellLayout layout = mAllAppsScreens.get(screenId); return layout; } /** * remove AppsCustomizeCellLayoutView */ public void removeAllViews() { int count = getChildCount(); for (int i = 0; i < count; i++) { AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(i); layout.removeAllViewsOnPage(); } } class ReorderAlarmListener implements OnAlarmListener { float[] dragViewCenter; int minSpanX, minSpanY, spanX, spanY; DragView dragView; View child; public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX, int spanY, DragView dragView, View child) { this.dragViewCenter = dragViewCenter; this.minSpanX = minSpanX; this.minSpanY = minSpanY; this.spanX = spanX; this.spanY = spanY; this.child = child; this.dragView = dragView; } public void onAlarm(Alarm alarm) { int[] resultSpan = new int[2]; mTargetCell = mDragUtils.findNearestArea((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout, mTargetCell); mDragUtils.mLastReorderX = mTargetCell[0]; mDragUtils.mLastReorderY = mTargetCell[1]; mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER); if (mTargetCell[0] < 0 || mTargetCell[1] < 0) { mDragTargetLayout.revertTempState(); } else { mDragUtils.setDragMode(mDragUtils.DRAG_MODE_REORDER); } boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY; mDragTargetLayout.visualizeDropLocation(child, mDragOutline, (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragView.getDragVisualizeOffset(), dragView.getDragRegion()); } } }