android 4.0自定义Folder--UFolder
===============
LauncherSettings & Favorites 增加
static final int ITEM_TYPE_UFOLDER = 5;
用以区分普通Folder
1.在各个地方针对ufolder进行判断,进行定制
1)//加载default_worksapce.xml,并记录到数据库
LauncherProvider.java
private long addUFolder(SQLiteDatabase db, ContentValues values) {
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_UFOLDER);
values.put(Favorites.SPANX, 1);
values.put(Favorites.SPANY, 1);
long id = generateNewId();
values.put(Favorites._ID, id);
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
return -1;
} else {
return id;
}
}
private int loadCustomFolder(SQLiteDatabase db, int workspaceResourceId) {
...
if (TAG_UFOLDER.equals(name)) {
String title;
int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
if (titleResId != -1) {
title = mContext.getResources().getString(titleResId);
} else {
title = mContext.getResources().getString(R.string.folder_name);
}
values.put(LauncherSettings.Favorites.TITLE, title);
long folderId = addUFolder(db, values);
added = folderId >= 0;
ArrayList<Long> folderItems = new ArrayList<Long>();
values.clear();
values.put(LauncherSettings.Favorites.CONTAINER, folderId);
// get the download app list
getCustomAppList();
for (AppInfo appInfo : appList) {
long id = addCustumAppShortcut(db, values, packageManager, intent, appInfo);
if (id >= 0) {
folderItems.add(id);
}
}
// We can only have folders with >= 2 items, so we need to remove the
// folder and clean up if less than 2 items were included, or some
// failed to add, and less than 2 were actually added
//if (folderItems.size() < 2 && folderId >= 0) {
// We just delete the folder and any items that made it
/*deleteId(db, folderId);
if (folderItems.size() > 0) {
deleteId(db, folderItems.get(0));
}
added = false;*/
//}
}
.....
}
//获取系统下载的app列表
private ArrayList<AppInfo> appList = new ArrayList<AppInfo>();
private void getCustomAppList() {
List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES);
for (int i = 0; i < packages.size(); i++) {
PackageInfo packageInfo = packages.get(i);
if ((packageInfo.applicationInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
ActivityInfo aInfo;
try {
aInfo = packageInfo.activities[0];
} catch (Exception e) {
Log.w(TAG, "Got exception for getCustomAppList", e);
continue;
}
AppInfo tmpInfo = new AppInfo();
tmpInfo.appName = packageInfo.applicationInfo.loadLabel(mContext.getPackageManager()).toString();
tmpInfo.appIcon = packageInfo.applicationInfo.loadIcon(mContext.getPackageManager());
tmpInfo.packageName = packageInfo.packageName;
tmpInfo.versionName = packageInfo.versionName;
tmpInfo.versionCode = packageInfo.versionCode;
if (aInfo != null) tmpInfo.className = aInfo.name;
// Only display the non-system app info
appList.add(tmpInfo);
}
}
}
//把列表插入数据库,LauncherModel loadWorkspace 就能加载了
private long addCustumAppShortcut(SQLiteDatabase db, ContentValues values,
PackageManager packageManager, Intent intent, AppInfo appInfo) {
long id = -1;
ActivityInfo info;
String packageName = appInfo.packageName;
String className = appInfo.className;
try {
ComponentName cn;
try {
cn = new ComponentName(packageName, className);
info = packageManager.getActivityInfo(cn, 0);
} catch (PackageManager.NameNotFoundException nnfe) {
String[] packages = packageManager.currentToCanonicalPackageNames(
new String[] { packageName });
cn = new ComponentName(packages[0], className);
info = packageManager.getActivityInfo(cn, 0);
}
id = generateNewId();
intent.setComponent(cn);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
values.put(Favorites.INTENT, intent.toUri(0));
values.put(Favorites.TITLE, appInfo.appName);
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
values.put(Favorites.SPANX, 1);
values.put(Favorites.SPANY, 1);
values.put(Favorites._ID, generateNewId());
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
return -1;
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to add favorite: " + packageName +
"/" + className, e);
}
return id;
}
2) 加载ufolder
LauncherModel.java
private void loadWorkspace() {
.....
while (!mStopped && c.moveToNext()) {
try {
int itemType = c.getInt(itemTypeIndex);
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
...
case LauncherSettings.Favorites.ITEM_TYPE_UFOLDER:
isUFolder = true;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sFolders, id);
//设置 ufolder 的itemType
// 这里用了个boolean isUFolder 判断,因为好像只有case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
// 才能生成folder.(应该会后更好的解决方法)
if (isUFolder) folderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_UFOLDER;
folderInfo.title = c.getString(titleIndex);
folderInfo.id = id;
container = c.getInt(containerIndex);
folderInfo.container = container;
folderInfo.screen = c.getInt(screenIndex);
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, folderInfo)) {
break;
}
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
sWorkspaceItems.add(folderInfo);
break;
}
sItemsIdMap.put(folderInfo.id, folderInfo);
sFolders.put(folderInfo.id, folderInfo);
isUFolder = false;
break;
........
}
}
3)定义 ufloder 图标
Launcher.java
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
setLoadOnResume();
final Workspace workspace = mWorkspace;
for (int i=start; i<end; i++) {
final ItemInfo item = shortcuts.get(i);
// Short circuit if we are loading dock items for a configuration which has no dock
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
mHotseat == null) {
continue;
}
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
View shortcut = createShortcut((ShortcutInfo)item);
workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
item.cellY, 1, 1, false);
break;
case LauncherSettings.Favorites.ITEM_TYPE_UFOLDER:
//ufolder icon layout ufolder_icon.xml
FolderIcon newUFolder = FolderIcon.fromXml(R.layout.ufolder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);
// 就是通过这在workspace中显示folder的
workspace.addInScreen(newUFolder, item.container, item.screen, item.cellX,
item.cellY, 1, 1, false);
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);
workspace.addInScreen(newFolder, item.container, item.screen, item.cellX,
item.cellY, 1, 1, false);
break;
}
}
workspace.requestLayout();
}
Workspace.java
private void onDropExternal(final int[] touchXY, final Object dragInfo,
final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (info.container == NO_ID && info instanceof ApplicationInfo) {
// Came from all apps -- make a copy
info = new ShortcutInfo((ApplicationInfo) info);
}
view = mLauncher.createShortcut(R.layout.application, cellLayout,
(ShortcutInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_UFOLDER:
view = FolderIcon.fromXml(R.layout.ufolder_icon, mLauncher, cellLayout,
(FolderInfo) info, mIconCache);
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
(FolderInfo) info, mIconCache);
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
}
}
===============
2.ufolder 的其他额外功能
1)当自定义的folder,所包含的内容<=1时,不从Launcher界面删除,也不删除数据库的记录
Folder.java
private void replaceFolderWithFinalItem() {
if (mInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_UFOLDER)
return;
ItemInfo finalItem = null;
if (getItemCount() == 1) {
finalItem = mInfo.contents.get(0);
}
// Remove the folder completely
CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container, mInfo.screen);
cellLayout.removeView(mFolderIcon);
if (mFolderIcon instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) mFolderIcon);
}
mLauncher.removeFolder(mInfo);
if (finalItem != null) {
LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
mInfo.screen, mInfo.cellX, mInfo.cellY);
}
LauncherModel.deleteItemFromDatabase(mLauncher, mInfo);
// Add the last remaining child to the workspace in place of the folder
if (finalItem != null) {
View child = mLauncher.createShortcut(R.layout.application, cellLayout,
(ShortcutInfo) finalItem);
mLauncher.getWorkspace().addInScreen(child, mInfo.container, mInfo.screen, mInfo.cellX,
mInfo.cellY, mInfo.spanX, mInfo.spanY);
}
}
2).ufolder onDrop 时,不能删除。
在workspace中移动ufolder时,不显示DeleteDropTarget。就是那个 (X Remove) 图标
DeleteDropTarget extends ButtonDropTarget
public void onDragStart(DragSource source, Object info, int dragAction) {
boolean isVisible = true;
boolean isUninstall = false;
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
if (folderInfo.itemType == 5) {
isVisible = false;
}
}
......
}
// 删除对应的DragObject
private void completeDrop(DragObject d) {
ItemInfo item = (ItemInfo) d.dragInfo;
if (isAllAppsApplication(d.dragSource, item)) {
// Uninstall the application if it is being dragged from AppsCustomize
mLauncher.startApplicationUninstallActivity((ApplicationInfo) item);
} else if (isWorkspaceOrFolderApplication(d)) {
LauncherModel.deleteItemFromDatabase(mLauncher, item);
} else if (isWorkspaceFolder(d)) {
// Remove the folder from the workspace and delete the contents from launcher model
FolderInfo folderInfo = (FolderInfo) item;
mLauncher.removeFolder(folderInfo);
LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo);
} else if (isWorkspaceOrFolderWidget(d)) {
// Remove the widget from the workspace
mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
LauncherModel.deleteItemFromDatabase(mLauncher, item);
final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
if (appWidgetHost != null) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
new Thread("deleteAppWidgetId") {
public void run() {
appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
}
}.start();
}
}
}
3).监控app install事件,ufolder 自动添加刚安装的app
Lancher.java
/**
* A package was installed.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
setLoadOnResume();
removeDialog(DIALOG_CREATE_SHORTCUT);
mWorkspace.addItems(apps);
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.addApps(apps);
}
}
Workspace.java
void addItems(final ArrayList<ApplicationInfo> apps) {
final ArrayList<ShortcutInfo> addContentInfo = new ArrayList<ShortcutInfo>();
final int appCount = apps.size();
for (int i = 0; i < appCount; i++) {
addContentInfo.add(apps.get(i).makeShortcut());
}
ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
for (final CellLayout layoutParent: cellLayouts) {
final ViewGroup layout = layoutParent.getChildrenLayout();
// Avoid ANRs by treating each screen separately
post(new Runnable() {
public void run() {
final ArrayList<View> childrenToAdd = new ArrayList<View>();
childrenToAdd.clear();
int childCount = layout.getChildCount();
for (int j = 0; j < childCount; j++) {
final View view = layout.getChildAt(j);
Object tag = view.getTag();
if (tag instanceof FolderInfo) {
final FolderInfo info = (FolderInfo) tag;
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_UFOLDER) {
for (ShortcutInfo appInfo : addContentInfo) {
// add in folder and insert data to database
info.add(appInfo);
}
}
}
}
}
});
}
}
===================
其他有关Folder的记录:
1.FolderIcon.java
computePreviewItemDrawingParams()
计算Folder上预览图标的参数,在dispatchDraw()中绘制,默认画三个。
2.两个shortcut拖到一起时会形成个folder
Workspace.java
createUserFolderIfNecessary();
在onDrogOver()中
if (userFolderPending && dragOverView != mLastDragOverView) {
mFolderCreationAlarm.setOnAlarmListener(new
FolderCreationAlarmListener(mDragTargetLayout, mTargetCell[0], mTargetCell[1]));
mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
}
public void onAlarm(Alarm alarm) {
if (mDragFolderRingAnimator == null) {
mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
}
mDragFolderRingAnimator.setCell(cellX, cellY);
mDragFolderRingAnimator.setCellLayout(layout);
mDragFolderRingAnimator.animateToAcceptState();
Resources res = mLauncher.getResources();
//生成folder的动画
FolderRingAnimator.sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
FolderRingAnimator.sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
layout.showFolderAccept(mDragFolderRingAnimator);
mCreateUserFolderOnDrop = true;
}