Redo the launcher loading code and put the real app icons into rollo.

This commit is contained in:
Joe Onorato 2009-08-17 11:03:03 -04:00
parent 02e638e6bf
commit 9c1289cb3b
22 changed files with 2071 additions and 1977 deletions

View File

@ -19,7 +19,10 @@
#define STATE_FLING_TIME 2
#define STATE_FLING_VELOCITY_X 3
#define STATE_ADJUSTED_DECELERATION 4
#define STATE_CURRENT_SCROLL_X 5 /* with fling offset applied */
/* with fling offset applied */
#define STATE_CURRENT_SCROLL_X 5
#define STATE_FLING_DURATION 6
#define STATE_FLING_END_POS 7
@ -36,6 +39,7 @@
#define PAGE_PADDING_TOP_PX 80
#define CELL_PADDING_TOP_PX 5
#define ICON_HEIGHT_PX 64
#define ICON_TEXTURE_HEIGHT_PX 128
#define ICON_LABEL_GUTTER_PX 5
#define CELL_PADDING_BOTTOM_PX 5
#define ROW_GUTTER_PX 10
@ -43,6 +47,7 @@
#define PAGE_PADDING_LEFT_PX 22
#define CELL_WIDTH_PX 105
#define ICON_WIDTH_PX 64
#define ICON_TEXTURE_WIDTH_PX 128
#define COLUMN_GUTTER_PX 5
#define LABEL_WIDTH_PX 105
@ -88,11 +93,14 @@ main(int launchID)
+ CELL_PADDING_BOTTOM_PX + ROW_GUTTER_PX) * densityScale;
float cellPaddingTop = CELL_PADDING_TOP_PX * densityScale;
float iconHeight = ICON_HEIGHT_PX * densityScale;
float iconTextureHeight = ICON_HEIGHT_PX / ((float)ICON_TEXTURE_HEIGHT_PX);
float iconLabelGutter = ICON_LABEL_GUTTER_PX * densityScale;
float pagePaddingLeft = PAGE_PADDING_LEFT_PX * densityScale;
float cellWidth = CELL_WIDTH_PX * densityScale;
float iconWidth = ICON_WIDTH_PX * densityScale;
float iconTextureWidth = ICON_WIDTH_PX / ((float)ICON_TEXTURE_WIDTH_PX);
float columnGutter = COLUMN_GUTTER_PX * densityScale;
float labelWidth = loadI32(ALLOC_PARAMS, PARAM_BUBBLE_WIDTH) * densityScale;
@ -128,6 +136,17 @@ main(int launchID)
} else {
deceleration = 1000;
}
// minimum velocity
if (flingVelocityPxMs < 0) {
if (flingVelocityPxMs > -500) {
flingVelocityPxMs = -500;
}
} else {
if (flingVelocityPxMs < 500) {
flingVelocityPxMs = 500;
}
}
// v' = v + at --> t = -v / a
// x' = x + vt + .5 a t^2
flingDurationMs = - flingVelocityPxMs / deceleration;
@ -142,26 +161,27 @@ main(int launchID)
}
float scrollOnPage = modf(endPos, SCREEN_WIDTH);
int endPage = -endPos/SCREEN_WIDTH;
if (flingVelocityPxMs < 0) {
if (scrollOnPage < (SCREEN_WIDTH/2)) {
// adjust the deceleration so we align on the page boundary
// a = 2(x-x0-v0t)/t^2
endPos = -(endPage+1) * SCREEN_WIDTH;
debugI32("endPos case", 1);
debugI32("endPos case 1", endPos);
} else {
// TODO: bounce
endPos = -(endPage+1) * SCREEN_WIDTH;
debugI32("endPos case", 2);
debugI32("endPos case 2", endPos);
}
} else {
if (scrollOnPage >= (SCREEN_WIDTH/2)) {
// adjust the deceleration so we align on the page boundary
endPos = -endPage * SCREEN_WIDTH;
debugI32("endPos case", 3);
debugI32("endPos case 3", endPos);
} else {
// TODO: bounce
endPos = -endPage * SCREEN_WIDTH;
debugI32("endPos case", 4);
debugI32("endPos case 4", endPos);
}
}
// v = v0 + at --> (v - v0) / t
@ -213,18 +233,10 @@ main(int launchID)
int currentPage = current_page(scrollXPx);
float screenWidth = SCREEN_WIDTH * densityScale;
float pageLeft = -1 + ((currentPage-1)*screenWidth);
int iconsPerPage = COLUMNS_PER_PAGE * ROWS_PER_PAGE;
int icon = (currentPage-1) * iconsPerPage;
if (icon < 0) {
icon = 0;
}
int page;
int lastIcon = icon + (iconsPerPage*3);
if (lastIcon >= iconCount) {
lastIcon = iconCount-1;
}
pageLeft += scrollXPx * densityScale;
int icon = 0;
int lastIcon = iconCount-1;
float pageLeft = -1 + (scrollXPx * densityScale);
while (icon <= lastIcon) {
// Bug makes 1.0f alpha fail.
color(1.0f, 1.0f, 1.0f, 0.99f);
@ -246,11 +258,16 @@ main(int launchID)
bindProgramFragmentStore(NAMED_PFS);
bindTexture(NAMED_PF, 0, loadI32(ALLOC_ICON_IDS, icon));
drawRect(iconLeft, iconTop, iconRight, iconBottom, 0.0f);
//drawRect(iconLeft, iconTop, iconRight, iconBottom, 0.0f);
drawQuadTexCoords(
iconLeft, iconTop, 0.0f, 0.0f, 0.0f,
iconRight, iconTop, 0.0f, iconTextureWidth, 0.0f,
iconRight, iconBottom, 0.0f, iconTextureWidth, iconTextureHeight,
iconLeft, iconBottom, 0.0f, 0.0f, iconTextureHeight);
// label
float labelLeft = s + ((cellWidth-labelWidth)/2.0f);
float labelTop = iconBottom - iconLabelGutter;
float labelTop = iconTop - iconHeight - iconLabelGutter;
bindProgramFragment(NAMED_PFText);
bindProgramFragmentStore(NAMED_PFSText);

View File

@ -402,4 +402,4 @@ public class ActivityPicker extends AlertActivity implements
return icon;
}
}
}
}

View File

@ -0,0 +1,216 @@
/*
* 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.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;
/**
* Stores the list of all applications for the all apps view.
*/
class AllAppsList {
public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
/** The list off all apps. */
public ArrayList<ApplicationInfo> data = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);
/** The list of apps that have been added since the last notify() call. */
public ArrayList<ApplicationInfo> added = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);
/** The list of apps that have been removed since the last notify() call. */
public ArrayList<ApplicationInfo> removed = new ArrayList();
/** The list of apps that have been modified since the last notify() call. */
public ArrayList<ApplicationInfo> modified = new ArrayList();
/**
* Boring constructor.
*/
public AllAppsList() {
}
/**
* Add the supplied ApplicationInfo objects to the list, and enqueue it into the
* list to broadcast when notify() is called.
*/
public void add(ApplicationInfo info) {
data.add(info);
added.add(info);
}
public void clear() {
data.clear();
// TODO: do we clear these too?
added.clear();
removed.clear();
modified.clear();
}
public int size() {
return data.size();
}
public ApplicationInfo get(int index) {
return data.get(index);
}
/**
* Add the icons for the supplied apk called packageName.
*/
public void addPackage(Context context, String packageName) {
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);
data.add(item);
added.add(item);
}
}
}
/**
* Remove the apps for the given apk identified by packageName.
*/
public void removePackage(String packageName) {
final List<ApplicationInfo> data = this.data;
for (int i=data.size()-1; i>=0; i--) {
ApplicationInfo info = data.get(i);
final ComponentName component = info.intent.getComponent();
if (packageName.equals(component.getPackageName())) {
removed.add(info);
data.remove(i);
}
}
// This is more aggressive than it needs to be.
AppInfoCache.flush();
}
/**
* Add and remove icons for this package which has been updated.
*/
public void updatePackage(Context context, String packageName) {
final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
if (matches.size() > 0) {
// Find disabled/removed activities and remove them from data and add them
// to the removed list.
for (int i=data.size()-1; i>=0; i--) {
final ApplicationInfo applicationInfo = data.get(i);
final ComponentName component = applicationInfo.intent.getComponent();
if (packageName.equals(component.getPackageName())) {
if (!findActivity(matches, component)) {
removed.add(applicationInfo);
AppInfoCache.remove(component);
data.remove(i);
}
}
}
// 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);
ApplicationInfo applicationInfo = findApplicationInfoLocked(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
if (applicationInfo == null) {
applicationInfo = AppInfoCache.cache(info, context, bubble);
data.add(applicationInfo);
added.add(applicationInfo);
} else {
AppInfoCache.update(info, applicationInfo, context);
modified.add(applicationInfo);
}
}
}
}
/**
* Query the package manager for MAIN/LAUNCHER activities in the supplied package.
*/
private static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
final PackageManager packageManager = context.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
final List<ResolveInfo> matches = new ArrayList<ResolveInfo>();
if (apps != null) {
// Find all activities that match the packageName
int count = apps.size();
for (int i = 0; i < count; i++) {
final ResolveInfo info = apps.get(i);
final ActivityInfo activityInfo = info.activityInfo;
if (packageName.equals(activityInfo.packageName)) {
matches.add(info);
}
}
}
return matches;
}
/**
* Returns whether <em>apps</em> contains <em>component</em>.
*/
private static boolean findActivity(List<ResolveInfo> apps, ComponentName component) {
final String className = component.getClassName();
for (ResolveInfo info : apps) {
final ActivityInfo activityInfo = info.activityInfo;
if (activityInfo.name.equals(className)) {
return true;
}
}
return false;
}
/**
* Find an ApplicationInfo object for the given packageName and className.
*/
private ApplicationInfo findApplicationInfoLocked(String packageName, String className) {
for (ApplicationInfo info: data) {
final ComponentName component = info.intent.getComponent();
if (packageName.equals(component.getPackageName())
&& className.equals(component.getClassName())) {
return info;
}
}
return null;
}
}

View File

@ -59,8 +59,11 @@ import android.graphics.PixelFormat;
public class AllAppsView extends RSSurfaceView {
private static final String TAG = "Launcher.AllAppsView";
private RenderScript mRS;
private RolloRS mRollo;
private ArrayList<ApplicationInfo> mAllAppsList;
private ViewConfiguration mConfig;
private VelocityTracker mVelocity;
@ -97,6 +100,9 @@ public class AllAppsView extends RSSurfaceView {
mRS = createRenderScript();
mRollo = new RolloRS();
mRollo.init(getResources(), w, h);
if (mAllAppsList != null) {
mRollo.setApps(mAllAppsList);
}
}
@Override
@ -109,7 +115,6 @@ public class AllAppsView extends RSSurfaceView {
@Override
public boolean onTouchEvent(MotionEvent ev)
{
Log.d(Launcher.LOG_TAG, "onTouchEvent " + ev);
int x = (int)ev.getX();
int deltaX;
switch (ev.getAction()) {
@ -163,10 +168,17 @@ public class AllAppsView extends RSSurfaceView {
DataSetObserver mIconObserver = new DataSetObserver() {
public void onChanged() {
Log.d(Launcher.LOG_TAG, "new icons arrived! now have " + mAdapter.getCount());
Log.d(TAG, "new icons arrived! now have " + mAdapter.getCount());
}
};
public void setApps(ArrayList<ApplicationInfo> list) {
mAllAppsList = list;
if (mRollo != null) {
mRollo.setApps(list);
}
}
public class RolloRS {
// Allocations ======
@ -316,22 +328,9 @@ public class AllAppsView extends RSSurfaceView {
}
private void initData() {
final int count = 100;
mParams = new Params(mRS);
mState = new State(mRS);
mIcons = new Allocation[count];
mAllocIconIDBuf = new int[count];
mAllocIconID = Allocation.createSized(mRS,
Element.USER_I32, mAllocIconIDBuf.length);
mLabels = new Allocation[count];
mAllocLabelIDBuf = new int[mLabels.length];
mAllocLabelID = Allocation.createSized(mRS,
Element.USER_I32, mLabels.length);
Element ie8888 = Element.RGBA_8888;
final Utilities.BubbleText bubble = new Utilities.BubbleText(getContext());
mParams.bubbleWidth = bubble.getBubbleWidth();
@ -339,25 +338,10 @@ public class AllAppsView extends RSSurfaceView {
mParams.bubbleBitmapWidth = bubble.getBitmapWidth();
mParams.bubbleBitmapHeight = bubble.getBitmapHeight();
for (int i=0; i<count; i++) {
mIcons[i] = Allocation.createFromBitmapResource(
mRS, mRes, R.raw.maps, ie8888, true);
mLabels[i] = makeTextBitmap(bubble, i%9==0 ? "Google Maps" : "Maps");
}
for (int i=0; i<count; i++) {
mIcons[i].uploadToTexture(0);
mLabels[i].uploadToTexture(0);
mAllocIconIDBuf[i] = mIcons[i].getID();
mAllocLabelIDBuf[i] = mLabels[i].getID();
}
mAllocIconID.data(mAllocIconIDBuf);
mAllocLabelID.data(mAllocLabelIDBuf);
mState.iconCount = count;
mParams.save();
mState.save();
setApps(null);
}
Allocation makeTextBitmap(Utilities.BubbleText bubble, String label) {
@ -383,8 +367,51 @@ public class AllAppsView extends RSSurfaceView {
mRS.contextBindRootScript(mScript);
}
}
private void setApps(ArrayList<ApplicationInfo> list) {
final int count = list != null ? list.size() : 0;
mIcons = new Allocation[count];
mAllocIconIDBuf = new int[count];
mAllocIconID = Allocation.createSized(mRS, Element.USER_I32, count);
mLabels = new Allocation[count];
mAllocLabelIDBuf = new int[count];
mAllocLabelID = Allocation.createSized(mRS, Element.USER_I32, count);
Element ie8888 = Element.RGBA_8888;
Utilities.BubbleText bubble = new Utilities.BubbleText(getContext());
for (int i=0; i<count; i++) {
final ApplicationInfo item = list.get(i);
mIcons[i] = Allocation.createFromBitmap(mRS, item.iconBitmap,
Element.RGBA_8888, true);
mLabels[i] = Allocation.createFromBitmap(mRS, item.titleBitmap,
Element.RGBA_8888, true);
mIcons[i].uploadToTexture(0);
mLabels[i].uploadToTexture(0);
mAllocIconIDBuf[i] = mIcons[i].getID();
mAllocLabelIDBuf[i] = mLabels[i].getID();
}
mAllocIconID.data(mAllocIconIDBuf);
mAllocLabelID.data(mAllocLabelIDBuf);
mState.iconCount = count;
Log.d("AllAppsView", "mScript=" + mScript + " mAllocIconID=" + mAllocIconID);
if (mScript != null) { // wtf
mScript.bindAllocation(mAllocIconID, Defines.ALLOC_ICON_IDS);
mScript.bindAllocation(mAllocLabelID, Defines.ALLOC_LABEL_IDS);
}
mState.save();
}
}
}

View File

@ -0,0 +1,174 @@
/*
* 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) {
synchronized (sCache) {
updateTitleAndIcon(info, applicationInfo, context, new Utilities.BubbleText(context));
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) {
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()) {
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;
}
Drawable icon = Utilities.createIconThumbnail(info.activityInfo.loadIcon(packageManager),
context, true);
application.iconBitmap = ((FastBitmapDrawable)icon).getBitmap();
application.filtered = true;
application.titleBitmap = bubble.createTextBitmap(application.title.toString());
}
}

View File

@ -33,6 +33,11 @@ class ApplicationInfo extends ItemInfo {
*/
CharSequence title;
/**
* A bitmap of the application's text in the bubble.
*/
Bitmap titleBitmap;
/**
* The intent used to start the application.
*/
@ -43,6 +48,11 @@ class ApplicationInfo extends ItemInfo {
*/
Drawable icon;
/**
* A bitmap version of the application icon.
*/
Bitmap iconBitmap;
/**
* When set to true, indicates that the icon has been resized.
*/
@ -124,4 +134,10 @@ class ApplicationInfo extends ItemInfo {
public String toString() {
return title.toString();
}
@Override
void unbind() {
super.unbind();
icon.setCallback(null);
}
}

View File

@ -28,7 +28,7 @@ import java.util.ArrayList;
/**
* GridView adapter to show the list of applications and shortcuts
*/
public class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> {
public class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> {
private final LayoutInflater mInflater;
public ApplicationsAdapter(Context context, ArrayList<ApplicationInfo> apps) {
@ -45,7 +45,7 @@ public class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> {
}
if (!info.filtered) {
info.icon = Utilities.createIconThumbnail(info.icon, getContext());
info.icon = Utilities.createIconThumbnail(info.icon, getContext(), false);
info.filtered = true;
}

View File

@ -63,7 +63,6 @@ public class BubbleTextView extends TextView {
setFocusable(true);
mBackground = getBackground();
setBackgroundDrawable(null);
mBackground.setCallback(this);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
@ -131,4 +130,16 @@ public class BubbleTextView extends TextView {
super.draw(canvas);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mBackground.setCallback(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mBackground.setCallback(null);
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.util.Log;
import java.util.LinkedList;
/**
* Queue of things to run on a looper thread. Items posted with {@link #post} will not
* be actually enqued on the handler until after the last one has run, to keep from
* starving the thread.
*
* This class is fifo.
*/
public class DeferredHandler {
private LinkedList<Runnable> mQueue = new LinkedList();
private MessageQueue mMessageQueue = Looper.myQueue();
private Impl mHandler = new Impl();
private class Impl extends Handler implements MessageQueue.IdleHandler {
public void handleMessage(Message msg) {
Runnable r;
synchronized (mQueue) {
r = mQueue.removeFirst();
}
r.run();
synchronized (mQueue) {
scheduleNextLocked();
}
}
public boolean queueIdle() {
handleMessage(null);
return false;
}
}
private class IdleRunnable implements Runnable {
Runnable mRunnable;
IdleRunnable(Runnable r) {
mRunnable = r;
}
public void run() {
mRunnable.run();
}
}
public DeferredHandler() {
}
/** Schedule runnable to run after everything that's on the queue right now. */
public void post(Runnable runnable) {
synchronized (mQueue) {
mQueue.add(runnable);
if (mQueue.size() == 1) {
scheduleNextLocked();
}
}
}
/** Schedule runnable to run when the queue goes idle. */
public void postIdle(final Runnable runnable) {
post(new IdleRunnable(runnable));
}
public void cancel() {
synchronized (mQueue) {
mQueue.clear();
}
}
void scheduleNextLocked() {
if (mQueue.size() > 0) {
Runnable peek = mQueue.getFirst();
if (peek instanceof IdleRunnable) {
mMessageQueue.addIdleHandler(mHandler);
} else {
mHandler.sendEmptyMessage(1);
}
}
}
}

View File

@ -93,24 +93,23 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
if (item.container == -1) return;
final LauncherModel model = Launcher.getModel();
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (item instanceof LauncherAppWidgetInfo) {
model.removeDesktopAppWidget((LauncherAppWidgetInfo) item);
} else {
model.removeDesktopItem(item);
mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
}
} else {
if (source instanceof UserFolder) {
final UserFolder userFolder = (UserFolder) source;
final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo();
model.removeUserFolderItem(userFolderInfo, item);
// item must be an ApplicationInfo otherwise it couldn't have been in the folder
// in the first place.
userFolderInfo.remove((ApplicationInfo)item);
}
}
if (item instanceof UserFolderInfo) {
final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
model.removeUserFolder(userFolderInfo);
mLauncher.removeFolder(userFolderInfo);
} else if (item instanceof LauncherAppWidgetInfo) {
final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();

View File

@ -273,8 +273,10 @@ public class DragController {
* Call this from a drag source view.
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(Launcher.LOG_TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
+ mDragging);
if (false) {
Log.d(Launcher.LOG_TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
+ mDragging);
}
final int action = ev.getAction();
final float screenX = ev.getRawX();

View File

@ -48,7 +48,7 @@ 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);
d = Utilities.createIconThumbnail(d, launcher, false);
icon.mCloseIcon = d;
icon.mOpenIcon = resources.getDrawable(R.drawable.ic_launcher_folder_open);
icon.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);

View File

@ -130,4 +130,6 @@ class ItemInfo {
}
}
void unbind() {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -50,4 +50,11 @@ class LauncherAppWidgetInfo extends ItemInfo {
public String toString() {
return Integer.toString(appWidgetId);
}
@Override
void unbind() {
super.unbind();
hostView = null;
}
}

View File

@ -17,13 +17,74 @@
package com.android.launcher2;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.os.Handler;
import dalvik.system.VMRuntime;
public class LauncherApplication extends Application {
public static final LauncherModel sModel = new LauncherModel();
@Override
public void onCreate() {
VMRuntime.getRuntime().setMinimumHeapSize(4 * 1024 * 1024);
super.onCreate();
// Register intent receivers
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
registerReceiver(mApplicationsReceiver, filter);
// Register for changes to the favorites
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
mFavoritesObserver);
}
/**
* There's no guarantee that this function is ever called.
*/
@Override
public void onTerminate() {
super.onTerminate();
unregisterReceiver(mApplicationsReceiver);
ContentResolver resolver = getContentResolver();
resolver.unregisterContentObserver(mFavoritesObserver);
}
/**
* Receives notifications when applications are added/removed.
*/
private final BroadcastReceiver mApplicationsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
sModel.onReceiveIntent(LauncherApplication.this, intent);
}
};
/**
* Receives notifications whenever the user favorites have changed.
*/
private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
// TODO: lockAllApps();
sModel.setWorkspaceDirty();
sModel.startLoader(LauncherApplication.this, false);
}
};
LauncherModel setLauncher(Launcher launcher) {
sModel.initialize(launcher);
return sModel;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -154,7 +154,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 = Utilities.createIconThumbnail(resources.getDrawable(id), mContext,
false);
mIcons.put(resource, icon);
} catch (Exception e) {
// Ignore

View File

@ -41,8 +41,8 @@ public class LiveFolderIcon extends FolderIcon {
final Resources resources = launcher.getResources();
Drawable d = folderInfo.icon;
if (d == null) {
d = Utilities.createIconThumbnail(
resources.getDrawable(R.drawable.ic_launcher_folder), launcher);
d = Utilities.createIconThumbnail(resources.getDrawable(R.drawable.ic_launcher_folder),
launcher, false);
folderInfo.filtered = true;
}
icon.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);

View File

@ -43,7 +43,7 @@ class UserFolderInfo extends FolderInfo {
}
/**
* Remove an app or shortcut
* Remove an app or shortcut. Does not change the DB.
*
* @param item
*/

View File

@ -78,82 +78,101 @@ final class Utilities {
* The size of the thumbnail is defined by the dimension
* android.R.dimen.launcher_application_icon_size.
*
* This method is not thread-safe and should be invoked on the UI thread only.
*
* @param icon The icon to get a thumbnail of.
* @param context The application's context.
* @param forceBitmap If this is true, the drawable will always be redrawn
* into a new bitmap if it isn't already a BitmapDrawable or a
* FastBitmapDrawable.
*
* @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) {
if (sIconWidth == -1) {
final Resources resources = context.getResources();
sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
}
int width = sIconWidth;
int height = sIconHeight;
float scale = 1.0f;
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());
static Drawable createIconThumbnail(Drawable icon, Context context, boolean forceBitmap) {
synchronized (sCanvas) { // we share the statics :-(
if (sIconWidth == -1) {
final Resources resources = context.getResources();
sIconWidth = (int)resources.getDimension(android.R.dimen.app_icon_size);
sIconHeight = sIconWidth;
}
}
int iconWidth = icon.getIntrinsicWidth();
int iconHeight = icon.getIntrinsicHeight();
if (width > 0 && height > 0) {
if (width < iconWidth || height < iconHeight || scale != 1.0f) {
final float ratio = (float) iconWidth / iconHeight;
int width = sIconWidth;
int height = sIconHeight;
if (iconWidth > iconHeight) {
height = (int) (width / ratio);
} else if (iconHeight > iconWidth) {
width = (int) (height * ratio);
float scale = 1.0f;
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();
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) {
if (iconWidth > 0 && iconWidth > 0) {
if (width < iconWidth || height < iconHeight || scale != 1.0f) {
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);
}
}
if (forceBitmap) {
// no intrinsic size --> use default size
int w = sIconWidth;
int h = sIconHeight;
final Bitmap.Config c = Bitmap.Config.ARGB_8888;
final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
final Bitmap thumb = Bitmap.createBitmap(roundToPow2(w), roundToPow2(h), 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.setBounds(0, 0, w, h);
icon.draw(canvas);
icon.setBounds(sOldBounds);
icon = new FastBitmapDrawable(thumb);
}
}
return icon;
return icon;
}
}
/**
@ -161,8 +180,6 @@ final class Utilities {
* The size of the thumbnail is defined by the dimension
* android.R.dimen.launcher_application_icon_size.
*
* This method is not thread-safe and should be invoked on the UI thread only.
*
* @param bitmap The bitmap to get a thumbnail of.
* @param context The application's context.
*
@ -170,42 +187,44 @@ final class Utilities {
* thumbnail could not be created.
*/
static Bitmap createBitmapThumbnail(Bitmap bitmap, Context context) {
if (sIconWidth == -1) {
final Resources resources = context.getResources();
sIconWidth = sIconHeight = (int) resources.getDimension(
android.R.dimen.app_icon_size);
}
int width = sIconWidth;
int height = sIconHeight;
final int bitmapWidth = bitmap.getWidth();
final int bitmapHeight = bitmap.getHeight();
if (width > 0 && height > 0 && (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);
synchronized (sCanvas) { // we share the statics :-(
if (sIconWidth == -1) {
final Resources resources = context.getResources();
sIconWidth = sIconHeight = (int) resources.getDimension(
android.R.dimen.app_icon_size);
}
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;
}
int width = sIconWidth;
int height = sIconHeight;
return bitmap;
final int bitmapWidth = bitmap.getWidth();
final int bitmapHeight = bitmap.getHeight();
if (width > 0 && height > 0 && (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;
}
return bitmap;
}
}
static class BubbleText {
@ -261,7 +280,6 @@ final class Utilities {
mFirstLineY = (int)(leading + ascent + 0.5f);
mLineHeight = (int)(leading + ascent + descent + 0.5f);
roundToPow2(64);
mBitmapWidth = roundToPow2((int)(mBubbleRect.width() + 0.5f));
mBitmapHeight = roundToPow2((int)((MAX_LINES * mLineHeight) + leading + 0.5f));

View File

@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.ComponentName;
import android.content.res.TypedArray;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.Rect;
@ -100,7 +101,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
private int[] mTempEstimate = new int[2];
private boolean mAllowLongPress;
private boolean mLocked;
private int mTouchSlop;
private int mMaximumVelocity;
@ -642,10 +642,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(Launcher.LOG_TAG, "Workspace onIntercept " + ev + " mLocked=" + mLocked
+ " mLauncher.isDrawerDown()=" + mLauncher.isDrawerDown());
if (mLocked || !mLauncher.isDrawerDown()) {
Log.d(Launcher.LOG_TAG, "returning false");
if (mLauncher.isWorkspaceLocked() || !mLauncher.isDrawerDown()) {
return false; // We don't want the events. Let them fall through to the all apps view.
}
@ -755,9 +752,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.d(Launcher.LOG_TAG, "Workspace onTouchEvent " + ev);
if (mLocked || !mLauncher.isDrawerDown()) {
if (mLauncher.isWorkspaceLocked() || !mLauncher.isDrawerDown()) {
return false; // We don't want the events. Let them fall through to the all apps view.
}
@ -990,8 +985,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
cellLayout.onDropChild(view, mTargetCell);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
final LauncherModel model = Launcher.getModel();
model.addDesktopItem(info);
LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
}
@ -1088,7 +1081,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
}
final Object tag = mDragInfo.cell.getTag();
Launcher.getModel().removeDesktopItem((ItemInfo) tag);
}
} else {
if (mDragInfo != null) {
@ -1186,24 +1178,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
return null;
}
/**
* Unlocks the SlidingDrawer so that touch events are processed.
*
* @see #lock()
*/
public void unlock() {
mLocked = false;
}
/**
* Locks the SlidingDrawer so that touch events are ignores.
*
* @see #unlock()
*/
public void lock() {
mLocked = true;
}
/**
* @return True is long presses are still allowed for the current touch
*/
@ -1221,7 +1195,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
void removeShortcutsForPackage(String packageName) {
final ArrayList<View> childrenToRemove = new ArrayList<View>();
final LauncherModel model = Launcher.getModel();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
@ -1244,7 +1217,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
name != null && packageName.equals(name.getPackageName())) {
model.removeDesktopItem(info);
LauncherModel.deleteItemFromDatabase(mLauncher, info);
childrenToRemove.add(view);
}
@ -1293,6 +1265,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
void updateShortcutsForPackage(String packageName) {
final PackageManager pm = mLauncher.getPackageManager();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final CellLayout layout = (CellLayout) getChildAt(i);
@ -1311,11 +1285,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
packageName.equals(name.getPackageName())) {
final Drawable icon = Launcher.getModel().getApplicationInfoIcon(
mLauncher.getPackageManager(), info);
final Drawable icon = AppInfoCache.getIconDrawable(pm, info);
if (icon != null && icon != info.icon) {
info.icon.setCallback(null);
info.icon = Utilities.createIconThumbnail(icon, mContext);
info.icon = Utilities.createIconThumbnail(icon, mContext, false);
info.filtered = true;
((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
info.icon, null, null);