一:核心类
Launcher3/src/com/android/launcher3/Launcher.java
Launcher3/src/com/android/launcher3/LauncherModel.java
二:加载桌面app图标流程
1:Launcher.java
@Override
protected void onCreate(Bundle savedInstanceState) {
...
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
...
}
mModel.startLoader(mWorkspace.getRestorePage());mModel是LauncherModel类,将会创建LoaderTask来加载app相关数据。
2:LauncherModel.java
public void startLoader(int synchronousBindPage) {
startLoader(synchronousBindPage, LOADER_FLAG_NONE);
}
public void startLoader(int synchronousBindPage, int loadFlags) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
if (DEBUG_LOADERS) {
LauncherLog.d(TAG, "startLoader: mCallbacks = " + mCallbacks);
}
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
// If there is already one running, tell it to stop.
stopLoaderLocked();
/// M: op01, 0p02, added for top package feature, load top packages from a xml file.
AllAppsListPluginEx.loadTopPackage(mApp.getContext());
mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
if (LauncherLog.DEBUG) {
LauncherLog.d(TAG, "startLoader: mAllAppsLoaded = " + mAllAppsLoaded
+ ",mWorkspaceLoaded = " + mWorkspaceLoaded + ",synchronousBindPage = "
+ synchronousBindPage + ",mIsLoaderTaskRunning = "
+ mIsLoaderTaskRunning + ",mLoaderTask = " + mLoaderTask);
}
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}
其中mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);这行代码将会开始加载app相关数据。
private class LoaderTask implements Runnable {
...
// Optimize for end-user experience: if the Launcher is up and // running with the
// All Apps interface in the foreground, load All Apps first. Otherwise, load the
// workspace first (default).
keep_running: {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
if (mStopped) {
LauncherLog.d(TAG, "LoadTask break in the middle, this = " + this);
break keep_running;
}
waitForIdle();
// second step
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
}
}
LoaderTask的代码很多,只贴了加载和绑定到wokespace的相关代码:如下所示
loadAndBindWorkspace();
private void loadAndBindWorkspace() {
mIsLoadingAndBindingWorkspace = true;
// Load the workspace
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
}
if (!mWorkspaceLoaded) {
loadWorkspace();
synchronized (LoaderTask.this) {
if (mStopped) {
LauncherLog.d(TAG, "loadAndBindWorkspace returned by stop flag.");
return;
}
mWorkspaceLoaded = true;
}
}
// Bind the workspace
bindWorkspace(-1);
}
/**
* Binds all loaded data to actual views on the main thread.
*/
private void bindWorkspace(int synchronizeBindPage) {
..
// Load items on the current page
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
currentFolders, null);
if (isLoadingSynchronously) {
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
callbacks.onPageBoundSynchronously(currentScreen);
}
}
};
runOnMainThread(r);
}
....
}
private void bindWorkspaceItems(final Callbacks oldCallbacks,
final ArrayList<ItemInfo> workspaceItems,
final ArrayList<LauncherAppWidgetInfo> appWidgets,
final LongArrayMap<FolderInfo> folders,
ArrayList<Runnable> deferredBindRunnables) {
final boolean postOnMainThread = (deferredBindRunnables != null);
Log.d("kaybicon", "bindWorkspaceItems: \n" + Log.getStackTraceString(new Throwable()));
// Bind the workspace items
int N = workspaceItems.size();
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
final Runnable r = new Runnable() {
@Override
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindItems(workspaceItems, start, start+chunkSize,
false);
}
}
};
if (postOnMainThread) {
synchronized (deferredBindRunnables) {
deferredBindRunnables.add(r);
}
} else {
runOnMainThread(r);
}
}
// Bind the folders
if (!folders.isEmpty()) {
final Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindFolders(folders);
}
}
};
if (postOnMainThread) {
synchronized (deferredBindRunnables) {
deferredBindRunnables.add(r);
}
} else {
runOnMainThread(r);
}
}
// Bind the widgets, one at a time
N = appWidgets.size();
for (int i = 0; i < N; i++) {
final LauncherAppWidgetInfo widget = appWidgets.get(i);
final Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAppWidget(widget);
}
}
};
if (postOnMainThread) {
deferredBindRunnables.add(r);
} else {
runOnMainThread(r);
}
}
}
其中这段代码的
callbacks.bindItems(workspaceItems, start, start+chunkSize,false);会创建相应的桌面图标对应的BubbleTextView并显示。callbacks指向的是Launcher.java 类.
// Bind the workspace items
int N = workspaceItems.size();
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
final Runnable r = new Runnable() {
@Override
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindItems(workspaceItems, start, start+chunkSize,
false);
}
}
};
if (postOnMainThread) {
synchronized (deferredBindRunnables) {
deferredBindRunnables.add(r);
}
} else {
runOnMainThread(r);
}
}
3:创建BubbleTextView
@Override
public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end,
final boolean forceAnimateIcons) {
Runnable r = new Runnable() {
public void run() {
bindItems(shortcuts, start, end, forceAnimateIcons);
}
};
if (waitUntilResume(r)) {
return;
}
// Get the list of added shortcuts and intersect them with the set of shortcuts here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection<Animator> bounceAnims = new ArrayList<Animator>();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
long newShortcutsScreenId = -1;
for (int i = start; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
if (LauncherLog.DEBUG) {
LauncherLog.d(TAG, "bindItems: start = " + start + ", end = " + end
+ "item = " + item + ", this = " + this);
}
// 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;
}
final View view;
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
ShortcutInfo info = (ShortcutInfo) item;
view = createShortcut(info);
/*
* TODO: FIX collision case
*/
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
View v = cl.getChildAt(item.cellX, item.cellY);
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
if (LauncherAppState.isDogfoodBuild()) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
}
}
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
view = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);
break;
default:
throw new RuntimeException("Invalid Item Type");
}
workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
item.cellY, 1, 1);
if (animateIcons) {
// Animate all the applications up now
view.setAlpha(0f);
view.setScaleX(0f);
view.setScaleY(0f);
bounceAnims.add(createNewAppBounceAnimation(view, i));
newShortcutsScreenId = item.screenId;
}
}
if (animateIcons) {
// Animate to the correct page
if (newShortcutsScreenId > -1) {
long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newShortcutsScreenId);
final Runnable startBounceAnimRunnable = new Runnable() {
public void run() {
anim.playTogether(bounceAnims);
anim.start();
}
};
if (newShortcutsScreenId != currentScreenId) {
// We post the animation slightly delayed to prevent slowdowns
// when we are loading right after we return to launcher.
mWorkspace.postDelayed(new Runnable() {
public void run() {
if (mWorkspace != null) {
mWorkspace.snapToPage(newScreenIndex);
mWorkspace.postDelayed(startBounceAnimRunnable,
NEW_APPS_ANIMATION_DELAY);
}
}
}, NEW_APPS_PAGE_MOVE_DELAY);
} else {
mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
}
}
}
workspace.requestLayout();
}
View createShortcut(ShortcutInfo info) {
return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
}
public View createShortcut(ViewGroup parent, ShortcutInfo info) {
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(defLayoutid,
parent, false);
favorite.applyFromShortcutInfo(info, mIconCache);
favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
favorite.setOnClickListener(this);
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
createShortcut(ViewGroup parent, ShortcutInfo info){}这个函数中将创建对应的BubbleTextView并显示在桌面上;而favorite.applyFromShortcutInfo(info, mIconCache);函数中会设置相应的icon图片和相应的名称。
public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
applyFromShortcutInfo(info, iconCache, false);
}
public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
boolean promiseStateChanged) {
Bitmap b = info.getIcon(iconCache);
FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
if (info.isDisabled()) {
iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
}
setIcon(iconDrawable, mIconSize);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
}
setText(info.title);
setTag(info);
if (promiseStateChanged || info.isPromise()) {
applyState(promiseStateChanged);
}
}
4:自定义要桌面要显示的图标和名称
修改方法:
1:在launcher3中添加相应的资源
2:修改applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, boolean promiseStateChanged){}函数中的setIcon():代码如下:
Drawable customIcon = Utils.getCustomerIcon(getContext(), info.intent);
if(customIcon != null){
setIcon(customIcon, mIconSize);
}else{
setIcon(iconDrawable, mIconSize);
}
public class Utils {
public static Map<String, Integer> mIconByPackageName = new HashMap<String, Integer>();
public static void initUtilities(){
mIconByPackageName.clear();
//icon
mIconByPackageName.put("xxxxx", R.drawable.xxx);
}
public static Drawable getCustomerIcon(Context context, Intent intent) {
if(intent == null){
return null;
}
try{
ComponentName component = intent.getComponent();
int resourceId;
if (mIconByComponentName.get(component) == null) {
String packageName = component.getPackageName();
if (mIconByPackageName.get(packageName) == null) {
return null;
} else {
resourceId = mIconByPackageName.get(packageName);
return context.getResources().getDrawable(resourceId);
}
} else {
resourceId = mIconByComponentName.get(component);
return context.getResources().getDrawable(resourceId);
}
}catch (Exception e){
return null;
}
}
}
Utils类是自定义的一个方法,mIconByPackageName是个Map,保存着 <app包名,图片资源id>的键值对, Utils.getCustomerIcon()根据包名获取相应的图片资源,如果存在则进行替换,否则保留最初的icon。