diff options
25 files changed, 632 insertions, 609 deletions
diff --git a/res/layout-land/workspace_screen.xml b/res/layout-land/workspace_screen.xml index 5349aa4b3..2483f5159 100644 --- a/res/layout-land/workspace_screen.xml +++ b/res/layout-land/workspace_screen.xml @@ -23,8 +23,8 @@ launcher:cellWidth="@dimen/workspace_cell_width" launcher:cellHeight="@dimen/workspace_cell_height" - launcher:longAxisStartPadding="@dimen/button_bar_height" - launcher:longAxisEndPadding="@dimen/button_bar_height" + launcher:longAxisStartPadding="65dip" + launcher:longAxisEndPadding="65dip" launcher:shortAxisStartPadding="0dip" launcher:shortAxisEndPadding="0dip" launcher:shortAxisCells="4" diff --git a/res/layout-port/workspace_screen.xml b/res/layout-port/workspace_screen.xml index 522d345cf..b43375b14 100644 --- a/res/layout-port/workspace_screen.xml +++ b/res/layout-port/workspace_screen.xml @@ -23,7 +23,7 @@ launcher:cellWidth="@dimen/workspace_cell_width" launcher:cellHeight="@dimen/workspace_cell_height" - launcher:longAxisStartPadding="0dip" + launcher:longAxisStartPadding="8dip" launcher:longAxisEndPadding="@dimen/button_bar_height" launcher:shortAxisStartPadding="0dip" launcher:shortAxisEndPadding="0dip" diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java index 00e9ea881..9d4c5b02a 100644 --- a/src/com/android/launcher2/AllAppsList.java +++ b/src/com/android/launcher2/AllAppsList.java @@ -44,10 +44,13 @@ class AllAppsList { /** The list of apps that have been modified since the last notify() call. */ public ArrayList<ApplicationInfo> modified = new ArrayList<ApplicationInfo>(); + private IconCache mIconCache; + /** * Boring constructor. */ - public AllAppsList() { + public AllAppsList(IconCache iconCache) { + mIconCache = iconCache; } /** @@ -82,9 +85,8 @@ class AllAppsList { final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName); if (matches.size() > 0) { - Utilities.BubbleText bubble = new Utilities.BubbleText(context); for (ResolveInfo info : matches) { - ApplicationInfo item = AppInfoCache.cache(info, context, bubble); + ApplicationInfo item = new ApplicationInfo(info, mIconCache); data.add(item); added.add(item); } @@ -105,9 +107,9 @@ class AllAppsList { } } // This is more aggressive than it needs to be. - AppInfoCache.flush(); + mIconCache.flush(); } - + /** * Add and remove icons for this package which has been updated. */ @@ -122,7 +124,7 @@ class AllAppsList { if (packageName.equals(component.getPackageName())) { if (!findActivity(matches, component)) { removed.add(applicationInfo); - AppInfoCache.remove(component); + mIconCache.remove(component); data.remove(i); } } @@ -130,7 +132,6 @@ class AllAppsList { // Find enabled activities and add them to the adapter // Also updates existing activities with new labels/icons - Utilities.BubbleText bubble = new Utilities.BubbleText(context); int count = matches.size(); for (int i = 0; i < count; i++) { final ResolveInfo info = matches.get(i); @@ -138,11 +139,12 @@ class AllAppsList { info.activityInfo.applicationInfo.packageName, info.activityInfo.name); if (applicationInfo == null) { - applicationInfo = AppInfoCache.cache(info, context, bubble); + applicationInfo = new ApplicationInfo(info, mIconCache); data.add(applicationInfo); added.add(applicationInfo); } else { - AppInfoCache.update(info, applicationInfo, context, bubble); + mIconCache.remove(applicationInfo.componentName); + mIconCache.getTitleAndIcon(applicationInfo, info); modified.add(applicationInfo); } } diff --git a/src/com/android/launcher2/AppInfoCache.java b/src/com/android/launcher2/AppInfoCache.java deleted file mode 100644 index a2d98307f..000000000 --- a/src/com/android/launcher2/AppInfoCache.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2008 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 android.content.ComponentName; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Intent; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; -import android.util.Log; -import android.os.Process; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * Cache of application icons. Icons can be made from any thread. - */ -public class AppInfoCache { - private static final String TAG = "Launcher.AppInfoCache"; - - private static final int INITIAL_ICON_CACHE_CAPACITY = 50; - - private static final HashMap<ComponentName, ApplicationInfo> sCache = - new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY); - - /** - * no public constructor. - */ - private AppInfoCache() { - } - - /** - * For the given ResolveInfo, return an ApplicationInfo and cache the result for later. - */ - public static ApplicationInfo cache(ResolveInfo info, Context context, - Utilities.BubbleText bubble) { - synchronized (sCache) { - ComponentName componentName = new ComponentName( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name); - ApplicationInfo application = sCache.get(componentName); - - if (application == null) { - application = new ApplicationInfo(); - application.container = ItemInfo.NO_ID; - - updateTitleAndIcon(info, application, context, bubble); - - application.setActivity(componentName, - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - - sCache.put(componentName, application); - } - - return application; - } - } - - /** - * Update the entry in the in the cache with its new metadata. - */ - public static void update(ResolveInfo info, ApplicationInfo applicationInfo, Context context, - Utilities.BubbleText bubble) { - synchronized (sCache) { - updateTitleAndIcon(info, applicationInfo, context, bubble); - - ComponentName componentName = new ComponentName( - info.activityInfo.applicationInfo.packageName, info.activityInfo.name); - sCache.put(componentName, applicationInfo); - } - } - - /** - * Remove any records for the supplied ComponentName. - */ - public static void remove(ComponentName componentName) { - synchronized (sCache) { - sCache.remove(componentName); - } - } - - /** - * Empty out the cache. - */ - public static void flush() { - synchronized (sCache) { - sCache.clear(); - } - } - - /** - * Get the icon for the supplied ApplicationInfo. If that activity already - * exists in the cache, use that. - */ - public static Drawable getIconDrawable(PackageManager packageManager, ApplicationInfo info) { - final ResolveInfo resolveInfo = packageManager.resolveActivity(info.intent, 0); - if (resolveInfo == null) { - return null; - } - - ComponentName componentName = new ComponentName( - resolveInfo.activityInfo.applicationInfo.packageName, - resolveInfo.activityInfo.name); - ApplicationInfo cached; - synchronized (sCache) { - cached = sCache.get(componentName); - if (cached != null) { - if (cached.icon == null) { - cached.icon = resolveInfo.activityInfo.loadIcon(packageManager); - } - return cached.icon; - } else { - return resolveInfo.activityInfo.loadIcon(packageManager); - } - } - } - - /** - * Go through the cache and disconnect any of the callbacks in the drawables or we - * leak the previous Home screen on orientation change. - */ - public static void unbindDrawables() { - synchronized (sCache) { - for (ApplicationInfo appInfo: sCache.values()) { - if (appInfo.icon != null) { - appInfo.icon.setCallback(null); - } - } - } - } - - /** - * Update the title and icon. Don't keep a reference to the context! - */ - private static void updateTitleAndIcon(ResolveInfo info, ApplicationInfo application, - Context context, Utilities.BubbleText bubble) { - final PackageManager packageManager = context.getPackageManager(); - - application.title = info.loadLabel(packageManager); - if (application.title == null) { - application.title = info.activityInfo.name; - } - - application.iconBitmap = Utilities.createAllAppsBitmap( - info.activityInfo.loadIcon(packageManager), context); - - application.titleBitmap = bubble.createTextBitmap(application.title.toString()); - } -} - diff --git a/src/com/android/launcher2/ApplicationInfo.java b/src/com/android/launcher2/ApplicationInfo.java index 0fe84e731..f50b88930 100644 --- a/src/com/android/launcher2/ApplicationInfo.java +++ b/src/com/android/launcher2/ApplicationInfo.java @@ -18,7 +18,9 @@ package com.android.launcher2; import android.content.ComponentName; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; +import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.util.Log; @@ -26,8 +28,7 @@ import android.util.Log; import java.util.ArrayList; /** - * Represents a launchable application. An application is made of a name (or title), - * an intent and an icon. + * Represents an app in AllAppsView. */ class ApplicationInfo extends ItemInfo { @@ -47,48 +48,37 @@ class ApplicationInfo extends ItemInfo { Intent intent; /** - * The application icon. - */ - Drawable icon; - - /** * A bitmap version of the application icon. */ Bitmap iconBitmap; - /** - * When set to true, indicates that the icon has been resized. - */ - boolean filtered; + ComponentName componentName; - /** - * Indicates whether the icon comes from an application's resource (if false) - * or from a custom Bitmap (if true.) - */ - boolean customIcon; + + ApplicationInfo() { + itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT; + } /** - * If isShortcut=true and customIcon=false, this contains a reference to the - * shortcut icon as an application's resource. + * Must not hold the Context. */ - Intent.ShortcutIconResource iconResource; + public ApplicationInfo(ResolveInfo info, IconCache iconCache) { + this.componentName = new ComponentName( + info.activityInfo.applicationInfo.packageName, + info.activityInfo.name); - ApplicationInfo() { - itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT; + this.container = ItemInfo.NO_ID; + this.setActivity(componentName, + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + + iconCache.getTitleAndIcon(this, info); } public ApplicationInfo(ApplicationInfo info) { super(info); + componentName = info.componentName; title = info.title.toString(); intent = new Intent(info.intent); - if (info.iconResource != null) { - iconResource = new Intent.ShortcutIconResource(); - iconResource.packageName = info.iconResource.packageName; - iconResource.resourceName = info.iconResource.resourceName; - } - icon = info.icon; - filtered = info.filtered; - customIcon = info.customIcon; } /** @@ -107,51 +97,20 @@ class ApplicationInfo extends ItemInfo { } @Override - void onAddToDatabase(ContentValues values) { - super.onAddToDatabase(values); - - String titleStr = title != null ? title.toString() : null; - values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr); - - String uri = intent != null ? intent.toUri(0) : null; - values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri); - - if (customIcon) { - values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, - LauncherSettings.BaseLauncherColumns.ICON_TYPE_BITMAP); - Bitmap bitmap = ((FastBitmapDrawable) icon).getBitmap(); - writeBitmap(values, bitmap); - } else { - values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, - LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE); - if (iconResource != null) { - values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, - iconResource.packageName); - values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE, - iconResource.resourceName); - } - } - } - - @Override public String toString() { return title.toString(); } - @Override - void unbind() { - super.unbind(); - icon.setCallback(null); - } - - public static void dumpApplicationInfoList(String tag, String label, ArrayList<ApplicationInfo> list) { Log.d(tag, label + " size=" + list.size()); for (ApplicationInfo info: list) { Log.d(tag, " title=\"" + info.title + "\" titleBitmap=" + info.titleBitmap - + " icon=" + info.icon + " iconBitmap=" + info.iconBitmap - + " filtered=" + info.filtered + " customIcon=" + info.customIcon); + + " iconBitmap=" + info.iconBitmap); } } + + public ShortcutInfo makeShortcut() { + return new ShortcutInfo(this); + } } diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java index ec4bc534c..fabeb4a3b 100644 --- a/src/com/android/launcher2/DeleteZone.java +++ b/src/com/android/launcher2/DeleteZone.java @@ -101,9 +101,9 @@ public class DeleteZone extends ImageView implements DropTarget, DragController. if (source instanceof UserFolder) { final UserFolder userFolder = (UserFolder) source; final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo(); - // item must be an ApplicationInfo otherwise it couldn't have been in the folder + // Item must be a ShortcutInfo otherwise it couldn't have been in the folder // in the first place. - userFolderInfo.remove((ApplicationInfo)item); + userFolderInfo.remove((ShortcutInfo)item); } } if (item instanceof UserFolderInfo) { diff --git a/src/com/android/launcher2/FastBitmapDrawable.java b/src/com/android/launcher2/FastBitmapDrawable.java index db2c01c4f..850535e67 100644 --- a/src/com/android/launcher2/FastBitmapDrawable.java +++ b/src/com/android/launcher2/FastBitmapDrawable.java @@ -67,6 +67,10 @@ class FastBitmapDrawable extends Drawable { return mBitmap.getHeight(); } + public void setBitmap(Bitmap b) { + mBitmap = b; + } + public Bitmap getBitmap() { return mBitmap; } diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java index 4f66ad052..7c35f7997 100644 --- a/src/com/android/launcher2/Folder.java +++ b/src/com/android/launcher2/Folder.java @@ -46,7 +46,7 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL /** * Which item is being dragged */ - protected ApplicationInfo mDragItem; + protected ShortcutInfo mDragItem; private boolean mCloneInfo; /** @@ -74,7 +74,7 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL } public void onItemClick(AdapterView parent, View v, int position, long id) { - ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position); + ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); mLauncher.startActivitySafely(app.intent); } @@ -93,9 +93,9 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL return false; } - ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position); + ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position); if (mCloneInfo) { - app = new ApplicationInfo(app); + app = new ShortcutInfo(app); } mDragController.startDrag(view, this, app, DragController.DRAG_ACTION_COPY); @@ -118,7 +118,7 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL /** * Sets the adapter used to populate the content area. The adapter must only - * contains ApplicationInfo items. + * contains ShortcutInfo items. * * @param adapter The list of applications to display in the folder. */ diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index 85fc3a754..826336c9f 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -48,7 +48,6 @@ public class FolderIcon extends BubbleTextView implements DropTarget { final Resources resources = launcher.getResources(); Drawable d = resources.getDrawable(R.drawable.ic_launcher_folder); - d = Utilities.createIconThumbnail(d, launcher); icon.mCloseIcon = d; icon.mOpenIcon = resources.getDrawable(R.drawable.ic_launcher_folder_open); icon.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null); @@ -77,8 +76,13 @@ public class FolderIcon extends BubbleTextView implements DropTarget { public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - final ApplicationInfo item = (ApplicationInfo) dragInfo; - // TODO: update open folder that is looking at this data + ShortcutInfo item; + if (dragInfo instanceof ApplicationInfo) { + // Came from all apps -- make a copy + item = ((ApplicationInfo)dragInfo).makeShortcut(); + } else { + item = (ShortcutInfo)dragInfo; + } mInfo.add(item); LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0); } diff --git a/src/com/android/launcher2/IconCache.java b/src/com/android/launcher2/IconCache.java new file mode 100644 index 000000000..847cab72e --- /dev/null +++ b/src/com/android/launcher2/IconCache.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2008 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 android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; +import android.util.Log; +import android.os.Process; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Cache of application icons. Icons can be made from any thread. + */ +public class IconCache { + private static final String TAG = "Launcher.IconCache"; + + private static final int INITIAL_ICON_CACHE_CAPACITY = 50; + + private static class CacheEntry { + public Bitmap icon; + public String title; + public Bitmap titleBitmap; + } + + private LauncherApplication mContext; + private PackageManager mPackageManager; + private Utilities.BubbleText mBubble; + private final HashMap<ComponentName, CacheEntry> mCache = + new HashMap<ComponentName, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY); + + public IconCache(LauncherApplication context) { + mContext = context; + mPackageManager = context.getPackageManager(); + mBubble = new Utilities.BubbleText(context); + } + + /** + * Remove any records for the supplied ComponentName. + */ + public void remove(ComponentName componentName) { + synchronized (mCache) { + mCache.remove(componentName); + } + } + + /** + * Empty out the cache. + */ + public void flush() { + synchronized (mCache) { + mCache.clear(); + } + } + + /** + * Fill in "application" with the icon and label for "info." + */ + public void getTitleAndIcon(ApplicationInfo application, ResolveInfo info) { + synchronized (mCache) { + CacheEntry entry = cacheLocked(application.componentName, info); + + application.title = entry.title; + application.titleBitmap = entry.titleBitmap; + application.iconBitmap = entry.icon; + } + } + + public Bitmap getIcon(Intent intent) { + final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0); + ComponentName component = intent.getComponent(); + + if (resolveInfo == null || component == null) { + return null; + } + + CacheEntry entry = cacheLocked(component, resolveInfo); + return entry.icon; + } + + public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo) { + if (resolveInfo == null || component == null) { + return null; + } + + CacheEntry entry = cacheLocked(component, resolveInfo); + return entry.icon; + } + + private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info) { + CacheEntry entry = mCache.get(componentName); + if (entry == null) { + entry = new CacheEntry(); + + entry.title = info.loadLabel(mPackageManager).toString(); + if (entry.title == null) { + entry.title = info.activityInfo.name; + } + entry.titleBitmap = mBubble.createTextBitmap(entry.title.toString()); + entry.icon = Utilities.createIconBitmap( + info.activityInfo.loadIcon(mPackageManager), mContext); + + mCache.put(componentName, entry); + } + return entry; + } +} + diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java index 45ff24e17..9a2f73fcd 100644 --- a/src/com/android/launcher2/InstallShortcutReceiver.java +++ b/src/com/android/launcher2/InstallShortcutReceiver.java @@ -63,7 +63,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // different places) boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); if (duplicate || !LauncherModel.shortcutExists(context, name, intent)) { - Launcher.addShortcut(context, data, cell, true); + ((LauncherApplication)context.getApplicationContext()).getModel() + .addShortcut(context, data, cell, true); Toast.makeText(context, context.getString(R.string.shortcut_installed, name), Toast.LENGTH_SHORT).show(); } else { diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java index f04880ddd..ca2ea86c8 100644 --- a/src/com/android/launcher2/ItemInfo.java +++ b/src/com/android/launcher2/ItemInfo.java @@ -112,21 +112,26 @@ class ItemInfo { } } + static byte[] flattenBitmap(Bitmap bitmap) { + // Try go guesstimate how much space the icon will take when serialized + // to avoid unnecessary allocations/copies during the write. + int size = bitmap.getWidth() * bitmap.getHeight() * 4; + ByteArrayOutputStream out = new ByteArrayOutputStream(size); + try { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w("Favorite", "Could not write icon"); + return null; + } + } + static void writeBitmap(ContentValues values, Bitmap bitmap) { if (bitmap != null) { - // Try go guesstimate how much space the icon will take when serialized - // to avoid unnecessary allocations/copies during the write. - int size = bitmap.getWidth() * bitmap.getHeight() * 4; - ByteArrayOutputStream out = new ByteArrayOutputStream(size); - try { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - out.flush(); - out.close(); - - values.put(LauncherSettings.Favorites.ICON, out.toByteArray()); - } catch (IOException e) { - Log.w("Favorite", "Could not write icon"); - } + byte[] data = flattenBitmap(bitmap); + values.put(LauncherSettings.Favorites.ICON, data); } } diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 646203c8c..089e5512b 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -191,6 +191,7 @@ public final class Launcher extends Activity private Bundle mSavedInstanceState; private LauncherModel mModel; + private IconCache mIconCache; private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>(); private static HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>(); @@ -202,7 +203,9 @@ public final class Launcher extends Activity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mModel = ((LauncherApplication)getApplication()).setLauncher(this); + LauncherApplication app = ((LauncherApplication)getApplication()); + mModel = app.setLauncher(this); + mIconCache = app.getIconCache(); mDragController = new DragController(this); mInflater = getLayoutInflater(); @@ -271,7 +274,7 @@ public final class Launcher extends Activity localeConfiguration.mnc = mnc; writeConfiguration(this, localeConfiguration); - AppInfoCache.flush(); + mIconCache.flush(); } } @@ -615,7 +618,7 @@ public final class Launcher extends Activity * * @return A View inflated from R.layout.application. */ - View createShortcut(ApplicationInfo info) { + View createShortcut(ShortcutInfo info) { return createShortcut(R.layout.application, (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info); } @@ -629,18 +632,12 @@ public final class Launcher extends Activity * * @return A View inflated from layoutResId. */ - View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) { + View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false); - if (info.icon == null) { - info.icon = AppInfoCache.getIconDrawable(getPackageManager(), info); - } - if (!info.filtered) { - info.icon = Utilities.createIconThumbnail(info.icon, this); - info.filtered = true; - } - - favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null); + favorite.setCompoundDrawablesWithIntrinsicBounds(null, + new FastBitmapDrawable(info.getIcon(mIconCache)), + null, null); favorite.setText(info.title); favorite.setTag(info); favorite.setOnClickListener(this); @@ -658,39 +655,17 @@ public final class Launcher extends Activity cellInfo.screen = mWorkspace.getCurrentScreen(); if (!findSingleSlot(cellInfo)) return; - final ApplicationInfo info = infoFromApplicationIntent(context, data); - if (info != null) { - mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked()); - } - } - - private static ApplicationInfo infoFromApplicationIntent(Context context, Intent data) { - ComponentName component = data.getComponent(); - PackageManager packageManager = context.getPackageManager(); - ActivityInfo activityInfo = null; - try { - activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */); - } catch (NameNotFoundException e) { - Log.e(TAG, "Couldn't find ActivityInfo for selected application", e); - } - - if (activityInfo != null) { - ApplicationInfo itemInfo = new ApplicationInfo(); + final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(), + data, context); - itemInfo.title = activityInfo.loadLabel(packageManager); - if (itemInfo.title == null) { - itemInfo.title = activityInfo.name; - } - - itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK | + if (info != null) { + info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - itemInfo.icon = activityInfo.loadIcon(packageManager); - itemInfo.container = ItemInfo.NO_ID; - - return itemInfo; + info.container = ItemInfo.NO_ID; + mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked()); + } else { + Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data); } - - return null; } /** @@ -703,7 +678,7 @@ public final class Launcher extends Activity cellInfo.screen = mWorkspace.getCurrentScreen(); if (!findSingleSlot(cellInfo)) return; - final ApplicationInfo info = addShortcut(this, data, cellInfo, false); + final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false); if (!mRestoring) { final View view = createShortcut(info); @@ -770,61 +745,6 @@ public final class Launcher extends Activity return mAppWidgetHost; } - static ApplicationInfo addShortcut(Context context, Intent data, - CellLayout.CellInfo cellInfo, boolean notify) { - - final ApplicationInfo info = infoFromShortcutIntent(context, data); - LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, - cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); - - return info; - } - - private static ApplicationInfo infoFromShortcutIntent(Context context, Intent data) { - Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); - String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); - Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); - - Drawable icon = null; - boolean filtered = false; - boolean customIcon = false; - ShortcutIconResource iconResource = null; - - if (bitmap != null && bitmap instanceof Bitmap) { - icon = new FastBitmapDrawable(Utilities.createBitmapThumbnail((Bitmap) bitmap, context)); - filtered = true; - customIcon = true; - } else { - Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); - if (extra != null && extra instanceof ShortcutIconResource) { - try { - iconResource = (ShortcutIconResource) extra; - final PackageManager packageManager = context.getPackageManager(); - Resources resources = packageManager.getResourcesForApplication( - iconResource.packageName); - final int id = resources.getIdentifier(iconResource.resourceName, null, null); - icon = resources.getDrawable(id); - } catch (Exception e) { - Log.w(TAG, "Could not load shortcut icon: " + extra); - } - } - } - - if (icon == null) { - icon = context.getPackageManager().getDefaultActivityIcon(); - } - - final ApplicationInfo info = new ApplicationInfo(); - info.icon = icon; - info.filtered = filtered; - info.title = name; - info.intent = intent; - info.customIcon = customIcon; - info.iconResource = iconResource; - - return info; - } - void closeSystemDialogs() { getWindow().closeAllPanels(); @@ -936,7 +856,6 @@ public final class Launcher extends Activity mModel.stopLoader(); unbindDesktopItems(); - AppInfoCache.unbindDrawables(); getContentResolver().unregisterContentObserver(mWidgetObserver); @@ -1163,7 +1082,6 @@ public final class Launcher extends Activity String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME); Drawable icon = null; - boolean filtered = false; Intent.ShortcutIconResource iconResource = null; Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON); @@ -1185,8 +1103,7 @@ public final class Launcher extends Activity } final LiveFolderInfo info = new LiveFolderInfo(); - info.icon = icon; - info.filtered = filtered; + info.icon = Utilities.createIconBitmap(icon, context); info.title = name; info.iconResource = iconResource; info.uri = data.getData(); @@ -1338,9 +1255,9 @@ public final class Launcher extends Activity */ public void onClick(View v) { Object tag = v.getTag(); - if (tag instanceof ApplicationInfo) { + if (tag instanceof ShortcutInfo) { // Open shortcut - final Intent intent = ((ApplicationInfo) tag).intent; + final Intent intent = ((ShortcutInfo)tag).intent; int[] pos = new int[2]; v.getLocationOnScreen(pos); intent.setSourceBounds( @@ -2011,7 +1928,7 @@ public final class Launcher extends Activity switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - final View shortcut = createShortcut((ApplicationInfo) item); + final View shortcut = createShortcut((ShortcutInfo)item); workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1, false); break; diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java index a72e53a21..183dbf571 100644 --- a/src/com/android/launcher2/LauncherApplication.java +++ b/src/com/android/launcher2/LauncherApplication.java @@ -25,11 +25,8 @@ import android.os.Handler; import dalvik.system.VMRuntime; public class LauncherApplication extends Application { - public final LauncherModel mModel; - - public LauncherApplication() { - mModel = new LauncherModel(this); - } + public LauncherModel mModel; + public IconCache mIconCache; @Override public void onCreate() { @@ -37,6 +34,9 @@ public class LauncherApplication extends Application { super.onCreate(); + mIconCache = new IconCache(this); + mModel = new LauncherModel(this, mIconCache); + // Register intent receivers IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -79,4 +79,12 @@ public class LauncherApplication extends Application { mModel.initialize(launcher); return mModel; } + + IconCache getIconCache() { + return mIconCache; + } + + LauncherModel getModel() { + return mModel; + } } diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 7eb240faf..16e5c3b6f 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -24,6 +24,7 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; +import android.content.Intent.ShortcutIconResource; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; @@ -34,6 +35,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; +import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; import android.os.Process; @@ -65,7 +67,10 @@ public class LauncherModel extends BroadcastReceiver { private boolean mBeforeFirstLoad = true; private WeakReference<Callbacks> mCallbacks; - private AllAppsList mAllAppsList = new AllAppsList(); + private AllAppsList mAllAppsList; + private IconCache mIconCache; + + private Bitmap mDefaultIcon; public interface Callbacks { public int getCurrentWorkspaceScreen(); @@ -80,8 +85,17 @@ public class LauncherModel extends BroadcastReceiver { public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps); } - LauncherModel(LauncherApplication app) { + LauncherModel(LauncherApplication app, IconCache iconCache) { mApp = app; + mAllAppsList = new AllAppsList(iconCache); + mIconCache = iconCache; + + mDefaultIcon = Utilities.createIconBitmap( + app.getPackageManager().getDefaultActivityIcon(), app); + } + + public Bitmap getDefaultIcon() { + return Bitmap.createBitmap(mDefaultIcon); } /** @@ -323,7 +337,7 @@ public class LauncherModel extends BroadcastReceiver { removed = mAllAppsList.removed; mAllAppsList.removed = new ArrayList<ApplicationInfo>(); for (ApplicationInfo info: removed) { - AppInfoCache.remove(info.intent.getComponent()); + mIconCache.remove(info.intent.getComponent()); } } if (mAllAppsList.modified.size() > 0) { @@ -658,7 +672,7 @@ public class LauncherModel extends BroadcastReceiver { final int displayModeIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.DISPLAY_MODE); - ApplicationInfo info; + ShortcutInfo info; String intentDescription; LauncherAppWidgetInfo appWidgetInfo; int container; @@ -680,15 +694,15 @@ public class LauncherModel extends BroadcastReceiver { } if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = getApplicationInfo(manager, intent, context); + info = getShortcutInfo(manager, intent, context); } else { - info = getApplicationInfoShortcut(c, context, iconTypeIndex, + info = getShortcutInfo(c, context, iconTypeIndex, iconPackageIndex, iconResourceIndex, iconIndex); } if (info == null) { - info = new ApplicationInfo(); - info.icon = manager.getDefaultActivityIcon(); + info = new ShortcutInfo(); + info.setIcon(getDefaultIcon()); } if (info != null) { @@ -973,9 +987,7 @@ public class LauncherModel extends BroadcastReceiver { return; } - final Context context = mContext; - final PackageManager packageManager = context.getPackageManager(); - + final PackageManager packageManager = mContext.getPackageManager(); final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0); synchronized (mLock) { @@ -986,10 +998,9 @@ public class LauncherModel extends BroadcastReceiver { long t = SystemClock.uptimeMillis(); int N = apps.size(); - Utilities.BubbleText bubble = new Utilities.BubbleText(context); for (int i=0; i<N && !mStopped; i++) { // This builds the icon bitmaps. - mAllAppsList.add(AppInfoCache.cache(apps.get(i), context, bubble)); + mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache)); } Collections.sort(mAllAppsList.data, APP_NAME_COMPARATOR); Collections.sort(mAllAppsList.added, APP_NAME_COMPARATOR); @@ -1050,9 +1061,9 @@ public class LauncherModel extends BroadcastReceiver { } /** - * Make an ApplicationInfo object for an application. + * Make an ShortcutInfo object for a sortcut that is an application. */ - private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent, + public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) { final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0); @@ -1060,26 +1071,26 @@ public class LauncherModel extends BroadcastReceiver { return null; } - final ApplicationInfo info = new ApplicationInfo(); + final ShortcutInfo info = new ShortcutInfo(); final ActivityInfo activityInfo = resolveInfo.activityInfo; - info.icon = Utilities.createIconThumbnail(activityInfo.loadIcon(manager), context); + info.setIcon(mIconCache.getIcon(intent.getComponent(), resolveInfo)); if (info.title == null || info.title.length() == 0) { info.title = activityInfo.loadLabel(manager); } if (info.title == null) { - info.title = ""; + info.title = activityInfo.name; } info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; return info; } /** - * Make an ApplicationInfo object for a sortcut + * Make an ShortcutInfo object for a shortcut that isn't an application. */ - private static ApplicationInfo getApplicationInfoShortcut(Cursor c, Context context, + private ShortcutInfo getShortcutInfo(Cursor c, Context context, int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex) { - final ApplicationInfo info = new ApplicationInfo(); + final ShortcutInfo info = new ShortcutInfo(); info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; int iconType = c.getInt(iconTypeIndex); @@ -1091,9 +1102,9 @@ public class LauncherModel extends BroadcastReceiver { try { Resources resources = packageManager.getResourcesForApplication(packageName); final int id = resources.getIdentifier(resourceName, null, null); - info.icon = Utilities.createIconThumbnail(resources.getDrawable(id), context); + info.setIcon(Utilities.createIconBitmap(resources.getDrawable(id), context)); } catch (Exception e) { - info.icon = packageManager.getDefaultActivityIcon(); + info.setIcon(getDefaultIcon()); } info.iconResource = new Intent.ShortcutIconResource(); info.iconResource.packageName = packageName; @@ -1104,23 +1115,74 @@ public class LauncherModel extends BroadcastReceiver { byte[] data = c.getBlob(iconIndex); try { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - info.icon = new FastBitmapDrawable( - Utilities.createBitmapThumbnail(bitmap, context)); + info.setIcon(bitmap); } catch (Exception e) { - packageManager = context.getPackageManager(); - info.icon = packageManager.getDefaultActivityIcon(); + info.setIcon(getDefaultIcon()); } - info.filtered = true; info.customIcon = true; break; default: - info.icon = context.getPackageManager().getDefaultActivityIcon(); + info.setIcon(getDefaultIcon()); info.customIcon = false; break; } return info; } + ShortcutInfo addShortcut(Context context, Intent data, + CellLayout.CellInfo cellInfo, boolean notify) { + + final ShortcutInfo info = infoFromShortcutIntent(context, data); + addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP, + cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify); + + return info; + } + + private ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { + Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); + String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); + + Bitmap icon = null; + boolean filtered = false; + boolean customIcon = false; + ShortcutIconResource iconResource = null; + + if (bitmap != null && bitmap instanceof Bitmap) { + icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context); + filtered = true; + customIcon = true; + } else { + Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); + if (extra != null && extra instanceof ShortcutIconResource) { + try { + iconResource = (ShortcutIconResource) extra; + final PackageManager packageManager = context.getPackageManager(); + Resources resources = packageManager.getResourcesForApplication( + iconResource.packageName); + final int id = resources.getIdentifier(iconResource.resourceName, null, null); + icon = Utilities.createIconBitmap(resources.getDrawable(id), context); + } catch (Exception e) { + Log.w(TAG, "Could not load shortcut icon: " + extra); + } + } + } + + if (icon == null) { + icon = getDefaultIcon(); + } + + final ShortcutInfo info = new ShortcutInfo(); + info.setIcon(icon); + info.title = name; + info.intent = intent; + info.customIcon = customIcon; + info.iconResource = iconResource; + + return info; + } + private static void loadLiveFolderIcon(Context context, Cursor c, int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, LiveFolderInfo liveFolderInfo) { @@ -1133,18 +1195,21 @@ public class LauncherModel extends BroadcastReceiver { try { Resources resources = packageManager.getResourcesForApplication(packageName); final int id = resources.getIdentifier(resourceName, null, null); - liveFolderInfo.icon = resources.getDrawable(id); + liveFolderInfo.icon = Utilities.createIconBitmap(resources.getDrawable(id), + context); } catch (Exception e) { - liveFolderInfo.icon = - context.getResources().getDrawable(R.drawable.ic_launcher_folder); + liveFolderInfo.icon = Utilities.createIconBitmap( + context.getResources().getDrawable(R.drawable.ic_launcher_folder), + context); } liveFolderInfo.iconResource = new Intent.ShortcutIconResource(); liveFolderInfo.iconResource.packageName = packageName; liveFolderInfo.iconResource.resourceName = resourceName; break; default: - liveFolderInfo.icon = - context.getResources().getDrawable(R.drawable.ic_launcher_folder); + liveFolderInfo.icon = Utilities.createIconBitmap( + context.getResources().getDrawable(R.drawable.ic_launcher_folder), + context); } } diff --git a/src/com/android/launcher2/LauncherProvider.java b/src/com/android/launcher2/LauncherProvider.java index f03e50c99..4a0802977 100644 --- a/src/com/android/launcher2/LauncherProvider.java +++ b/src/com/android/launcher2/LauncherProvider.java @@ -34,9 +34,12 @@ import android.content.pm.PackageManager; import android.content.pm.ActivityInfo; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; import android.database.sqlite.SQLiteQueryBuilder; import android.database.Cursor; import android.database.SQLException; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.util.Log; import android.util.Xml; import android.util.AttributeSet; @@ -61,7 +64,7 @@ public class LauncherProvider extends ContentProvider { private static final String DATABASE_NAME = "launcher.db"; - private static final int DATABASE_VERSION = 7; + private static final int DATABASE_VERSION = 8; static final String AUTHORITY = "com.android.launcher2.settings"; @@ -392,6 +395,14 @@ public class LauncherProvider extends ContentProvider { version = 7; } + if (version < 8) { + // Version 8 (froyo) has the icons all normalized. This should + // already be the case in practice, but we now rely on it and don't + // resample the images each time. + normalizeIcons(db); + version = 8; + } + if (version != DATABASE_VERSION) { Log.w(TAG, "Destroying all old data."); db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); @@ -466,6 +477,60 @@ public class LauncherProvider extends ContentProvider { return true; } + private void normalizeIcons(SQLiteDatabase db) { + Log.d(TAG, "normalizing icons"); + + Cursor c = null; + try { + boolean logged = false; + final ContentValues values = new ContentValues(); + final ContentResolver cr = mContext.getContentResolver(); + final SQLiteStatement update = db.compileStatement("UPDATE favorites " + + "WHERE _id=? SET icon=?"); + + c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" + + Favorites.ICON_TYPE_BITMAP, null); + + final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); + final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON); + + while (c.moveToNext()) { + long id = c.getLong(idIndex); + byte[] data = c.getBlob(iconIndex); + try { + Bitmap bitmap = Utilities.resampleIconBitmap( + BitmapFactory.decodeByteArray(data, 0, data.length), + mContext); + if (bitmap != null) { + update.bindLong(1, id); + data = ItemInfo.flattenBitmap(bitmap); + if (data != null) { + update.bindBlob(2, data); + update.execute(); + } + bitmap.recycle(); + bitmap = null; + } + } catch (Exception e) { + if (!logged) { + Log.e(TAG, "Failed normalizing icon " + id, e); + } else { + Log.e(TAG, "Also failed normalizing icon " + id); + } + logged = true; + } + } + } catch (SQLException ex) { + Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); + } finally { + db.endTransaction(); + if (c != null) { + c.close(); + } + } + + } + /** * Upgrade existing clock and photo frame widgets into their new widget * equivalents. diff --git a/src/com/android/launcher2/LiveFolderAdapter.java b/src/com/android/launcher2/LiveFolderAdapter.java index b0e9eff1f..58b43e33a 100644 --- a/src/com/android/launcher2/LiveFolderAdapter.java +++ b/src/com/android/launcher2/LiveFolderAdapter.java @@ -141,7 +141,12 @@ class LiveFolderAdapter extends CursorAdapter { if (icon == null) { final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - icon = new FastBitmapDrawable(Utilities.createBitmapThumbnail(bitmap, mContext)); + final Bitmap resampled = Utilities.resampleIconBitmap(bitmap, mContext); + if (bitmap != resampled) { + // If we got back a different object, we don't need the old one any more. + bitmap.recycle(); + } + icon = new FastBitmapDrawable(resampled); mCustomIcons.put(holder.id, new SoftReference<Drawable>(icon)); } } else if (holder.iconResourceIndex != -1 && holder.iconPackageIndex != -1) { @@ -154,7 +159,8 @@ class LiveFolderAdapter extends CursorAdapter { cursor.getString(holder.iconPackageIndex)); final int id = resources.getIdentifier(resource, null, null); - icon = Utilities.createIconThumbnail(resources.getDrawable(id), mContext); + icon = new FastBitmapDrawable( + Utilities.createIconBitmap(resources.getDrawable(id), mContext)); mIcons.put(resource, icon); } catch (Exception e) { // Ignore diff --git a/src/com/android/launcher2/LiveFolderIcon.java b/src/com/android/launcher2/LiveFolderIcon.java index 55f100cce..f80928bd4 100644 --- a/src/com/android/launcher2/LiveFolderIcon.java +++ b/src/com/android/launcher2/LiveFolderIcon.java @@ -21,7 +21,7 @@ import android.content.res.Resources; import android.util.AttributeSet; import android.view.ViewGroup; import android.view.LayoutInflater; -import android.graphics.drawable.Drawable; +import android.graphics.Bitmap; public class LiveFolderIcon extends FolderIcon { public LiveFolderIcon(Context context, AttributeSet attrs) { @@ -39,13 +39,12 @@ public class LiveFolderIcon extends FolderIcon { LayoutInflater.from(launcher).inflate(resId, group, false); final Resources resources = launcher.getResources(); - Drawable d = folderInfo.icon; - if (d == null) { - d = Utilities.createIconThumbnail(resources.getDrawable(R.drawable.ic_launcher_folder), + Bitmap b = folderInfo.icon; + if (b == null) { + b = Utilities.createIconBitmap(resources.getDrawable(R.drawable.ic_launcher_folder), launcher); - folderInfo.filtered = true; } - icon.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null); + icon.setCompoundDrawablesWithIntrinsicBounds(null, new FastBitmapDrawable(b), null, null); icon.setText(folderInfo.title); icon.setTag(folderInfo); icon.setOnClickListener(launcher); diff --git a/src/com/android/launcher2/LiveFolderInfo.java b/src/com/android/launcher2/LiveFolderInfo.java index 5b1217c15..7d0a0f58f 100644 --- a/src/com/android/launcher2/LiveFolderInfo.java +++ b/src/com/android/launcher2/LiveFolderInfo.java @@ -19,6 +19,7 @@ package com.android.launcher2; import android.content.ContentValues; import android.content.Intent; import android.graphics.drawable.Drawable; +import android.graphics.Bitmap; import android.net.Uri; class LiveFolderInfo extends FolderInfo { @@ -41,12 +42,7 @@ class LiveFolderInfo extends FolderInfo { /** * The live folder icon. */ - Drawable icon; - - /** - * When set to true, indicates that the icon has been resized. - */ - boolean filtered; + Bitmap icon; /** * Reference to the live folder icon as an application's resource. diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java new file mode 100644 index 000000000..cb73ac05b --- /dev/null +++ b/src/com/android/launcher2/ShortcutInfo.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008 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 android.content.ComponentName; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.util.Log; + +import java.util.ArrayList; + +/** + * Represents a launchable icon on the workspaces and in folders. + */ +class ShortcutInfo extends ItemInfo { + + /** + * The application name. + */ + CharSequence title; + + /** + * The intent used to start the application. + */ + Intent intent; + + /** + * Indicates whether the icon comes from an application's resource (if false) + * or from a custom Bitmap (if true.) + */ + boolean customIcon; + + /** + * If isShortcut=true and customIcon=false, this contains a reference to the + * shortcut icon as an application's resource. + */ + Intent.ShortcutIconResource iconResource; + + /** + * The application icon. + */ + private Bitmap mIcon; + + ShortcutInfo() { + itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT; + } + + public ShortcutInfo(ShortcutInfo info) { + super(info); + title = info.title.toString(); + intent = new Intent(info.intent); + if (info.iconResource != null) { + iconResource = new Intent.ShortcutIconResource(); + iconResource.packageName = info.iconResource.packageName; + iconResource.resourceName = info.iconResource.resourceName; + } + mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all + customIcon = info.customIcon; + } + + /** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */ + public ShortcutInfo(ApplicationInfo info) { + super(info); + title = info.title.toString(); + intent = new Intent(info.intent); + customIcon = false; + } + + public void setIcon(Bitmap b) { + mIcon = b; + } + + public Bitmap getIcon(IconCache iconCache) { + if (mIcon == null) { + mIcon = iconCache.getIcon(this.intent); + } + return mIcon; + } + + /** + * Creates the application intent based on a component name and various launch flags. + * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}. + * + * @param className the class name of the component representing the intent + * @param launchFlags the launch flags + */ + final void setActivity(ComponentName className, int launchFlags) { + intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setComponent(className); + intent.setFlags(launchFlags); + itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION; + } + + @Override + void onAddToDatabase(ContentValues values) { + super.onAddToDatabase(values); + + String titleStr = title != null ? title.toString() : null; + values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr); + + String uri = intent != null ? intent.toUri(0) : null; + values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri); + + if (customIcon) { + values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, + LauncherSettings.BaseLauncherColumns.ICON_TYPE_BITMAP); + Bitmap bitmap = this.mIcon; + writeBitmap(values, bitmap); + } else { + values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE, + LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE); + if (iconResource != null) { + values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, + iconResource.packageName); + values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE, + iconResource.resourceName); + } + } + } + + @Override + public String toString() { + return title.toString(); + } + + @Override + void unbind() { + super.unbind(); + } + + + public static void dumpShortcutInfoList(String tag, String label, + ArrayList<ShortcutInfo> list) { + Log.d(tag, label + " size=" + list.size()); + for (ShortcutInfo info: list) { + Log.d(tag, " title=\"" + info.title + " icon=" + info.mIcon + + " customIcon=" + info.customIcon); + } + } +} + diff --git a/src/com/android/launcher2/ApplicationsAdapter.java b/src/com/android/launcher2/ShortcutsAdapter.java index 129103af0..212b5d62d 100644 --- a/src/com/android/launcher2/ApplicationsAdapter.java +++ b/src/com/android/launcher2/ShortcutsAdapter.java @@ -29,34 +29,29 @@ import java.util.ArrayList; /** * GridView adapter to show the list of applications and shortcuts */ -public class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> { +public class ShortcutsAdapter extends ArrayAdapter<ShortcutInfo> { private final LayoutInflater mInflater; private final PackageManager mPackageManager; + private final IconCache mIconCache; - public ApplicationsAdapter(Context context, ArrayList<ApplicationInfo> apps) { + public ShortcutsAdapter(Context context, ArrayList<ShortcutInfo> apps) { super(context, 0, apps); mPackageManager = context.getPackageManager(); mInflater = LayoutInflater.from(context); + mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache(); } @Override public View getView(int position, View convertView, ViewGroup parent) { - final ApplicationInfo info = getItem(position); + final ShortcutInfo info = getItem(position); if (convertView == null) { convertView = mInflater.inflate(R.layout.application_boxed, parent, false); } - if (info.icon == null) { - info.icon = AppInfoCache.getIconDrawable(mPackageManager, info); - } - if (!info.filtered) { - info.icon = Utilities.createIconThumbnail(info.icon, getContext()); - info.filtered = true; - } - final TextView textView = (TextView) convertView; - textView.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null); + textView.setCompoundDrawablesWithIntrinsicBounds(null, + new FastBitmapDrawable(info.getIcon(mIconCache)), null, null); textView.setText(info.title); return convertView; diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java index 16fe6160c..148949217 100644 --- a/src/com/android/launcher2/UserFolder.java +++ b/src/com/android/launcher2/UserFolder.java @@ -46,13 +46,14 @@ public class UserFolder extends Folder implements DropTarget { public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - ApplicationInfo item = (ApplicationInfo) dragInfo; - if (item.container == NO_ID) { + ShortcutInfo item; + if (dragInfo instanceof ApplicationInfo) { // Came from all apps -- make a copy - item = new ApplicationInfo((ApplicationInfo)item); + item = ((ApplicationInfo)dragInfo).makeShortcut(); + } else { + item = (ShortcutInfo)dragInfo; } - //noinspection unchecked - ((ArrayAdapter<ApplicationInfo>) mContent.getAdapter()).add((ApplicationInfo) dragInfo); + ((ShortcutsAdapter)mContent.getAdapter()).add(item); LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, 0, 0); } @@ -71,16 +72,14 @@ public class UserFolder extends Folder implements DropTarget { @Override public void onDropCompleted(View target, boolean success) { if (success) { - //noinspection unchecked - ArrayAdapter<ApplicationInfo> adapter = - (ArrayAdapter<ApplicationInfo>) mContent.getAdapter(); + ShortcutsAdapter adapter = (ShortcutsAdapter)mContent.getAdapter(); adapter.remove(mDragItem); } } void bind(FolderInfo info) { super.bind(info); - setContentAdapter(new ApplicationsAdapter(mContext, ((UserFolderInfo) info).contents)); + setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents)); } // When the folder opens, we need to refresh the GridView's selection by diff --git a/src/com/android/launcher2/UserFolderInfo.java b/src/com/android/launcher2/UserFolderInfo.java index 75b19eec6..0b8841c10 100644 --- a/src/com/android/launcher2/UserFolderInfo.java +++ b/src/com/android/launcher2/UserFolderInfo.java @@ -27,7 +27,7 @@ class UserFolderInfo extends FolderInfo { /** * The apps and shortcuts */ - ArrayList<ApplicationInfo> contents = new ArrayList<ApplicationInfo>(); + ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>(); UserFolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER; @@ -38,7 +38,7 @@ class UserFolderInfo extends FolderInfo { * * @param item */ - public void add(ApplicationInfo item) { + public void add(ShortcutInfo item) { contents.add(item); } @@ -47,7 +47,7 @@ class UserFolderInfo extends FolderInfo { * * @param item */ - public void remove(ApplicationInfo item) { + public void remove(ShortcutInfo item) { contents.remove(item); } diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java index 30108af0b..93ff97af8 100644 --- a/src/com/android/launcher2/Utilities.java +++ b/src/com/android/launcher2/Utilities.java @@ -85,87 +85,6 @@ final class Utilities { return bitmap; } - /** - * Returns a Drawable representing the thumbnail of the specified Drawable. - * The size of the thumbnail is defined by the dimension - * android.R.dimen.launcher_application_icon_size. - * - * @param icon The icon to get a thumbnail of. - * @param context The application's context. - * - * @return A thumbnail for the specified icon or the icon itself if the - * thumbnail could not be created. - */ - static Drawable createIconThumbnail(Drawable icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } - - int width = sIconWidth; - int height = sIconHeight; - - if (icon instanceof PaintDrawable) { - PaintDrawable painter = (PaintDrawable) icon; - painter.setIntrinsicWidth(width); - painter.setIntrinsicHeight(height); - } else if (icon instanceof BitmapDrawable) { - // Ensure the bitmap has a density. - BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; - Bitmap bitmap = bitmapDrawable.getBitmap(); - if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { - bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics()); - } - } - int iconWidth = icon.getIntrinsicWidth(); - int iconHeight = icon.getIntrinsicHeight(); - - if (iconWidth > 0 && iconHeight > 0) { - if (width < iconWidth || height < iconHeight) { - final float ratio = (float) iconWidth / iconHeight; - - if (iconWidth > iconHeight) { - height = (int) (width / ratio); - } else if (iconHeight > iconWidth) { - width = (int) (height * ratio); - } - - final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ? - Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; - final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c); - final Canvas canvas = sCanvas; - canvas.setBitmap(thumb); - // Copy the old bounds to restore them later - // If we were to do oldBounds = icon.getBounds(), - // the call to setBounds() that follows would - // change the same instance and we would lose the - // old bounds - sOldBounds.set(icon.getBounds()); - final int x = (sIconWidth - width) / 2; - final int y = (sIconHeight - height) / 2; - icon.setBounds(x, y, x + width, y + height); - icon.draw(canvas); - icon.setBounds(sOldBounds); - icon = new FastBitmapDrawable(thumb); - } else if (iconWidth < width && iconHeight < height) { - final Bitmap.Config c = Bitmap.Config.ARGB_8888; - final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c); - final Canvas canvas = sCanvas; - canvas.setBitmap(thumb); - sOldBounds.set(icon.getBounds()); - final int x = (width - iconWidth) / 2; - final int y = (height - iconHeight) / 2; - icon.setBounds(x, y, x + iconWidth, y + iconHeight); - icon.draw(canvas); - icon.setBounds(sOldBounds); - icon = new FastBitmapDrawable(thumb); - } - } - - return icon; - } - } - static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff }; static int sColorIndex = 0; @@ -173,7 +92,7 @@ final class Utilities { * Returns a bitmap suitable for the all apps view. The bitmap will be a power * of two sized ARGB_8888 bitmap that can be used as a gl texture. */ - static Bitmap createAllAppsBitmap(Drawable icon, Context context) { + static Bitmap createIconBitmap(Drawable icon, Context context) { synchronized (sCanvas) { // we share the statics :-( if (sIconWidth == -1) { initStatics(context); @@ -279,55 +198,17 @@ final class Utilities { * @return A thumbnail for the specified bitmap or the bitmap itself if the * thumbnail could not be created. */ - static Bitmap createBitmapThumbnail(Bitmap bitmap, Context context) { + static Bitmap resampleIconBitmap(Bitmap bitmap, Context context) { synchronized (sCanvas) { // we share the statics :-( if (sIconWidth == -1) { initStatics(context); } - int width = sIconWidth; - int height = sIconHeight; - - final int bitmapWidth = bitmap.getWidth(); - final int bitmapHeight = bitmap.getHeight(); - - if (width > 0 && height > 0) { - if (width < bitmapWidth || height < bitmapHeight) { - final float ratio = (float) bitmapWidth / bitmapHeight; - - if (bitmapWidth > bitmapHeight) { - height = (int) (width / ratio); - } else if (bitmapHeight > bitmapWidth) { - width = (int) (height * ratio); - } - - final Bitmap.Config c = (width == sIconWidth && height == sIconHeight) ? - bitmap.getConfig() : Bitmap.Config.ARGB_8888; - final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c); - final Canvas canvas = sCanvas; - final Paint paint = sPaint; - canvas.setBitmap(thumb); - paint.setDither(false); - paint.setFilterBitmap(true); - sBounds.set((sIconWidth - width) / 2, (sIconHeight - height) / 2, width, height); - sOldBounds.set(0, 0, bitmapWidth, bitmapHeight); - canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint); - return thumb; - } else if (bitmapWidth < width || bitmapHeight < height) { - final Bitmap.Config c = Bitmap.Config.ARGB_8888; - final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c); - final Canvas canvas = sCanvas; - final Paint paint = sPaint; - canvas.setBitmap(thumb); - paint.setDither(false); - paint.setFilterBitmap(true); - canvas.drawBitmap(bitmap, (sIconWidth - bitmapWidth) / 2, - (sIconHeight - bitmapHeight) / 2, paint); - return thumb; - } + if (bitmap.getWidth() == sIconWidth && bitmap.getHeight() == sIconHeight) { + return bitmap; + } else { + return createIconBitmap(new BitmapDrawable(bitmap), context); } - - return bitmap; } } diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 2c95beea8..1d2d75e51 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -90,6 +90,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag private OnLongClickListener mLongClickListener; private Launcher mLauncher; + private IconCache mIconCache; private DragController mDragController; /** @@ -142,9 +143,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * Initializes various states for this workspace. */ private void initWorkspace() { - mScroller = new Scroller(getContext()); + Context context = getContext(); + mScroller = new Scroller(context); mCurrentScreen = mDefaultScreen; Launcher.setScreen(mCurrentScreen); + LauncherApplication app = (LauncherApplication)context.getApplicationContext(); + mIconCache = app.getIconCache(); final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); @@ -880,11 +884,11 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } - void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo) { + void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) { addApplicationShortcut(info, cellInfo, false); } - void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo, + void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo, boolean insertAtFirst) { final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen); final int[] result = new int[2]; @@ -950,10 +954,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: if (info.container == NO_ID) { // Came from all apps -- make a copy - info = new ApplicationInfo((ApplicationInfo) info); + info = new ShortcutInfo((ShortcutInfo)info); } - view = mLauncher.createShortcut(R.layout.application, cellLayout, - (ApplicationInfo) info); + view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info); break; case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, @@ -1183,8 +1186,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag final View view = layout.getChildAt(j); Object tag = view.getTag(); - if (tag instanceof ApplicationInfo) { - final ApplicationInfo info = (ApplicationInfo) tag; + if (tag instanceof ShortcutInfo) { + final ShortcutInfo info = (ShortcutInfo) tag; // We need to check for ACTION_MAIN otherwise getComponent() might // return null for some shortcuts (for instance, for shortcuts to // web pages.) @@ -1199,14 +1202,13 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } else if (tag instanceof UserFolderInfo) { final UserFolderInfo info = (UserFolderInfo) tag; - final ArrayList<ApplicationInfo> contents = info.contents; - final ArrayList<ApplicationInfo> toRemove = - new ArrayList<ApplicationInfo>(1); + final ArrayList<ShortcutInfo> contents = info.contents; + final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); final int contentsCount = contents.size(); boolean removedFromFolder = false; for (int k = 0; k < contentsCount; k++) { - final ApplicationInfo appInfo = contents.get(k); + final ShortcutInfo appInfo = contents.get(k); final Intent intent = appInfo.intent; final ComponentName name = intent.getComponent(); @@ -1277,8 +1279,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag for (int j = 0; j < childCount; j++) { final View view = layout.getChildAt(j); Object tag = view.getTag(); - if (tag instanceof ApplicationInfo) { - ApplicationInfo info = (ApplicationInfo) tag; + if (tag instanceof ShortcutInfo) { + ShortcutInfo info = (ShortcutInfo)tag; // We need to check for ACTION_MAIN otherwise getComponent() might // return null for some shortcuts (for instance, for shortcuts to // web pages.) @@ -1288,14 +1290,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag Intent.ACTION_MAIN.equals(intent.getAction()) && name != null && packageName.equals(name.getPackageName())) { - final Drawable icon = AppInfoCache.getIconDrawable(pm, info); - if (icon != null && icon != info.icon) { - info.icon.setCallback(null); - info.icon = Utilities.createIconThumbnail(icon, mContext); - info.filtered = true; - ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null, - info.icon, null, null); - } + info.setIcon(mIconCache.getIcon(info.intent)); + ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null, + new FastBitmapDrawable(info.getIcon(mIconCache)), null, null); } } } |