/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher2; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.graphics.Bitmap.Config; import android.graphics.Region.Op; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.provider.LiveFolders; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import com.android.launcher.R; public class CustomizePagedView extends PagedView implements View.OnLongClickListener, DragSource { public enum CustomizationType { WidgetCustomization, FolderCustomization, ShortcutCustomization, WallpaperCustomization } private static final String TAG = "CustomizeWorkspace"; private static final boolean DEBUG = false; private Launcher mLauncher; private DragController mDragController; private PackageManager mPackageManager; private CustomizationType mCustomizationType; private PagedViewCellLayout mTmpWidgetLayout; private ArrayList> mWidgetPages; private List mWidgetList; private List mFolderList; private List mShortcutList; private int mCellCountX; private int mCellCountY; private final Canvas mCanvas = new Canvas(); private final LayoutInflater mInflater; public CustomizePagedView(Context context) { this(context, null); } public CustomizePagedView(Context context, AttributeSet attrs) { super(context, attrs); mCellCountX = 8; mCellCountY = 4; mCustomizationType = CustomizationType.WidgetCustomization; mWidgetPages = new ArrayList>(); mTmpWidgetLayout = new PagedViewCellLayout(context); mInflater = LayoutInflater.from(context); setupPage(mTmpWidgetLayout); setVisibility(View.GONE); setSoundEffectsEnabled(false); } public void setLauncher(Launcher launcher) { Context context = getContext(); mLauncher = launcher; mPackageManager = context.getPackageManager(); } public void update() { Context context = getContext(); // get the list of widgets mWidgetList = AppWidgetManager.getInstance(mLauncher).getInstalledProviders(); Collections.sort(mWidgetList, new Comparator() { @Override public int compare(AppWidgetProviderInfo object1, AppWidgetProviderInfo object2) { return object1.label.compareTo(object2.label); } }); Comparator resolveInfoComparator = new Comparator() { @Override public int compare(ResolveInfo object1, ResolveInfo object2) { return object1.loadLabel(mPackageManager).toString().compareTo( object2.loadLabel(mPackageManager).toString()); } }; // get the list of live folder intents Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); mFolderList = mPackageManager.queryIntentActivities(liveFolderIntent, 0); // manually create a separate entry for creating a folder in Launcher ResolveInfo folder = new ResolveInfo(); folder.icon = R.drawable.ic_launcher_folder; folder.labelRes = R.string.group_folder; folder.resolvePackageName = context.getPackageName(); mFolderList.add(0, folder); Collections.sort(mFolderList, resolveInfoComparator); // get the list of shortcuts Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); mShortcutList = mPackageManager.queryIntentActivities(shortcutsIntent, 0); Collections.sort(mShortcutList, resolveInfoComparator); invalidatePageData(); } public void setDragController(DragController dragger) { mDragController = dragger; } public void setCustomizationFilter(CustomizationType filterType) { mCustomizationType = filterType; setCurrentScreen(0); invalidatePageData(); } @Override public void onDropCompleted(View target, boolean success) { // do nothing } @Override public boolean onLongClick(View v) { if (!v.isInTouchMode()) { return false; } final View animView = v; switch (mCustomizationType) { case WidgetCustomization: AppWidgetProviderInfo appWidgetInfo = (AppWidgetProviderInfo) v.getTag(); LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(appWidgetInfo.provider); dragInfo.minWidth = appWidgetInfo.minWidth; dragInfo.minHeight = appWidgetInfo.minHeight; mDragController.startDrag(v, this, dragInfo, DragController.DRAG_ACTION_COPY); mLauncher.hideCustomizationDrawer(); return true; case FolderCustomization: // animate some feedback to the long press animateClickFeedback(v, new Runnable() { @Override public void run() { // add the folder ResolveInfo resolveInfo = (ResolveInfo) animView.getTag(); Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); if (resolveInfo.labelRes == R.string.group_folder) { // Create app shortcuts is a special built-in case of shortcuts createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getContext().getString(R.string.group_folder)); } else { ComponentName name = new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); createFolderIntent.setComponent(name); } mLauncher.prepareAddItemFromHomeCustomizationDrawer(); mLauncher.addLiveFolder(createFolderIntent); } }); return true; case ShortcutCustomization: // animate some feedback to the long press animateClickFeedback(v, new Runnable() { @Override public void run() { // add the shortcut ResolveInfo info = (ResolveInfo) animView.getTag(); Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); if (info.labelRes == R.string.group_applications) { // Create app shortcuts is a special built-in case of shortcuts createShortcutIntent.putExtra( Intent.EXTRA_SHORTCUT_NAME,getContext().getString( R.string.group_applications)); } else { ComponentName name = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); createShortcutIntent.setComponent(name); } mLauncher.prepareAddItemFromHomeCustomizationDrawer(); mLauncher.processShortcut(createShortcutIntent); } }); return true; } return false; } private int relayoutWidgets() { final int widgetCount = mWidgetList.size(); if (widgetCount == 0) return 0; mWidgetPages.clear(); ArrayList page = new ArrayList(); mWidgetPages.add(page); int rowOffsetX = 0; int rowOffsetY = 0; int curRowHeight = 0; // we only get the cell dims this way for the layout calculations because // we know that we aren't going to change the dims when we construct it // afterwards for (int i = 0; i < widgetCount; ++i) { AppWidgetProviderInfo info = mWidgetList.get(i); PagedViewCellLayout.LayoutParams params; final int cellSpanX = mTmpWidgetLayout.estimateCellHSpan(info.minWidth); final int cellSpanY = mTmpWidgetLayout.estimateCellVSpan(info.minHeight); if (((rowOffsetX + cellSpanX) <= mCellCountX) && ((rowOffsetY + cellSpanY) <= mCellCountY)) { // just add to end of current row params = new PagedViewCellLayout.LayoutParams(rowOffsetX, rowOffsetY, cellSpanX, cellSpanY); rowOffsetX += cellSpanX; curRowHeight = Math.max(curRowHeight, cellSpanY); } else { /* // fix all the items in this last row to be bottom aligned int prevRowOffsetX = rowOffsetX; for (int j = page.size() - 1; j >= 0; --j) { PagedViewCellLayout.LayoutParams params = page.get(j); // skip once we get to the previous row if (params.cellX > prevRowOffsetX) break; params.cellY += curRowHeight - params.cellVSpan; prevRowOffsetX = params.cellX; } */ // doesn't fit on current row, see if we can start a new row on // this page if ((rowOffsetY + curRowHeight + cellSpanY) > mCellCountY) { // start a new page and add this item to it page = new ArrayList(); mWidgetPages.add(page); params = new PagedViewCellLayout.LayoutParams(0, 0, cellSpanX, cellSpanY); rowOffsetX = cellSpanX; rowOffsetY = 0; curRowHeight = cellSpanY; } else { // add it to the current page on this new row params = new PagedViewCellLayout.LayoutParams(0, rowOffsetY + curRowHeight, cellSpanX, cellSpanY); rowOffsetX = cellSpanX; rowOffsetY += curRowHeight; curRowHeight = cellSpanY; } } params.setTag(info); page.add(params); } return mWidgetPages.size(); } private Drawable getWidgetIcon(PagedViewCellLayout.LayoutParams params, AppWidgetProviderInfo info) { PackageManager packageManager = mLauncher.getPackageManager(); String packageName = info.provider.getPackageName(); Drawable drawable = null; if (info.previewImage != 0) { drawable = packageManager.getDrawable(packageName, info.previewImage, null); if (drawable == null) { Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) + " for provider: " + info.provider); } else { return drawable; } } // If we don't have a preview image, create a default one if (drawable == null) { Resources resources = mLauncher.getResources(); // Determine the size the widget will take in the layout // Create a new bitmap to hold the widget preview int[] dims = mTmpWidgetLayout.estimateCellDimensions(getMeasuredWidth(), getMeasuredHeight(), params.cellHSpan, params.cellVSpan); final int width = dims[0]; final int height = dims[1] - 35; // TEMP // TEMP: HACK ABOVE TO GET TEXT TO SHOW // TEMP Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); mCanvas.setBitmap(bitmap); // For some reason, we must re-set the clip rect here, otherwise it will be wrong mCanvas.clipRect(0, 0, width, height, Op.REPLACE); Drawable background = resources.getDrawable(R.drawable.default_widget_preview); background.setBounds(0, 0, width, height); background.draw(mCanvas); // Draw the icon vertically centered, flush left try { Rect tmpRect = new Rect(); Drawable icon = null; if (info.icon != 0) { icon = packageManager.getDrawable(packageName, info.icon, null); } else { icon = resources.getDrawable(R.drawable.ic_launcher_application); } background.getPadding(tmpRect); final int iconSize = Math.min( Math.min(icon.getIntrinsicWidth(), width - tmpRect.left - tmpRect.right), Math.min(icon.getIntrinsicHeight(), height - tmpRect.top - tmpRect.bottom)); final int left = (width / 2) - (iconSize / 2); final int top = (height / 2) - (iconSize / 2); icon.setBounds(new Rect(left, top, left + iconSize, top + iconSize)); icon.draw(mCanvas); } catch (Resources.NotFoundException e) { // if we can't find the icon, then just don't draw it } drawable = new BitmapDrawable(resources, bitmap); } drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); return drawable; } private void setupPage(PagedViewCellLayout layout) { layout.setCellCount(mCellCountX, mCellCountY); layout.setPadding(20, 10, 20, 0); } private void syncWidgetPages() { if (mWidgetList == null) return; // calculate the layout for all the widget pages first and ensure that // we have the right number of pages int numPages = relayoutWidgets(); int curNumPages = getChildCount(); // remove any extra pages after the "last" page int extraPageDiff = curNumPages - numPages; for (int i = 0; i < extraPageDiff; ++i) { removeViewAt(numPages); } // add any necessary pages for (int i = curNumPages; i < numPages; ++i) { PagedViewCellLayout layout = new PagedViewCellLayout(getContext()); setupPage(layout); addView(layout); } } private void syncWidgetPageItems(int page) { // ensure that we have the right number of items on the pages PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); final ArrayList list = mWidgetPages.get(page); final int count = list.size(); layout.removeAllViews(); for (int i = 0; i < count; ++i) { PagedViewCellLayout.LayoutParams params = list.get(i); AppWidgetProviderInfo info = (AppWidgetProviderInfo) params.getTag(); final Drawable icon = getWidgetIcon(params, info); TextView text = (TextView) mInflater.inflate(R.layout.customize_paged_view_widget, layout, false); text.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null); text.setText(info.label); text.setTag(info); text.setOnLongClickListener(this); layout.addViewToCellLayout(text, -1, mWidgetList.indexOf(info), params); } } private void syncListPages(List list) { // ensure that we have the right number of pages int numPages = (int) Math.ceil((float) list.size() / (mCellCountX * mCellCountY)); int curNumPages = getChildCount(); // remove any extra pages after the "last" page int extraPageDiff = curNumPages - numPages; for (int i = 0; i < extraPageDiff; ++i) { removeViewAt(numPages); } // add any necessary pages for (int i = curNumPages; i < numPages; ++i) { PagedViewCellLayout layout = new PagedViewCellLayout(getContext()); setupPage(layout); addView(layout); } } private void syncListPageItems(int page, List list) { // ensure that we have the right number of items on the pages int numCells = mCellCountX * mCellCountY; int startIndex = page * numCells; int endIndex = Math.min(startIndex + numCells, list.size()); PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); // TODO: we can optimize by just re-applying to existing views layout.removeAllViews(); for (int i = startIndex; i < endIndex; ++i) { ResolveInfo info = list.get(i); Drawable image = info.loadIcon(mPackageManager); TextView text = (TextView) mInflater.inflate(R.layout.customize_paged_view_item, layout, false); image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); text.setCompoundDrawablesWithIntrinsicBounds(null, image, null, null); text.setText(info.loadLabel(mPackageManager)); text.setTag(info); text.setOnLongClickListener(this); final int index = i - startIndex; final int x = index % mCellCountX; final int y = index / mCellCountX; layout.addViewToCellLayout(text, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1)); } } private void syncWallpaperPages() { // NOT CURRENTLY IMPLEMENTED // ensure that we have the right number of pages int numPages = 1; int curNumPages = getChildCount(); // remove any extra pages after the "last" page int extraPageDiff = curNumPages - numPages; for (int i = 0; i < extraPageDiff; ++i) { removeViewAt(numPages); } // add any necessary pages for (int i = curNumPages; i < numPages; ++i) { PagedViewCellLayout layout = new PagedViewCellLayout(getContext()); setupPage(layout); addView(layout); } } private void syncWallpaperPageItems(int page) { PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); layout.removeAllViews(); TextView text = (TextView) mInflater.inflate( R.layout.customize_paged_view_wallpaper_placeholder, layout, false); // NOTE: this is just place holder text until MikeJurka implements wallpaper picker text.setText("Wallpaper customization coming soon!"); layout.addViewToCellLayout(text, -1, 0, new PagedViewCellLayout.LayoutParams(0, 0, 3, 1)); } @Override public void syncPages() { switch (mCustomizationType) { case WidgetCustomization: syncWidgetPages(); break; case FolderCustomization: syncListPages(mFolderList); break; case ShortcutCustomization: syncListPages(mShortcutList); break; case WallpaperCustomization: syncWallpaperPages(); break; default: removeAllViews(); setCurrentScreen(0); break; } // only try and center the page if there is one page final int childCount = getChildCount(); if (childCount == 1) { PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(0); layout.enableCenteredContent(true); } else { for (int i = 0; i < childCount; ++i) { PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i); layout.enableCenteredContent(false); } } // bound the current page setCurrentScreen(Math.max(0, Math.min(childCount - 1, getCurrentScreen()))); } @Override public void syncPageItems(int page) { switch (mCustomizationType) { case WidgetCustomization: syncWidgetPageItems(page); break; case FolderCustomization: syncListPageItems(page, mFolderList); break; case ShortcutCustomization: syncListPageItems(page, mShortcutList); break; case WallpaperCustomization: syncWallpaperPageItems(page); break; } } }