Launcher,实现了Callbacks接口,提供了绑定应用图标、小部件、所有应用数据等方法
LauncherAppState,存储有InvariantDeviceProfile、LauncherModel、IconCache、Context对象,方便单例调用
InvariantDeviceProfile,桌面图标行数和列数、文件夹图标行数和列数等配置信息
LauncherModel,数据处理的核心类
LoaderTask,负责加载数据和绑定数据
LoaderResults、BaseLoaderResults,回调Launcher相关绑定方法
BgDataModel,存储从数据库中解析处理的数据,ItemInfo集合、LauncherAppWidgetInfo集合、FolderInfo集合
Launcher
private LauncherModel mModel;
@Override
@TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
...
//从LauncherAppState 获取LauncherModel对象
LauncherAppState app = LauncherAppState.getInstance(this);
mModel = app.getModel();
...
//触发LauncherModel的startLoader()方法调用
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
// If we are not binding synchronously, pause drawing until initial bind complete,
// so that the system could continue to show the device loading prompt
mOnInitialBindListener = Boolean.FALSE::booleanValue;
}
}
}
LauncherModel
private final ArrayList<Callbacks> mCallbacksList = new ArrayList<>(1);
public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
synchronized (mLock) {
//把参数callbacks添加到mCallbacksList集合中,Launcher实现了callbacks接口
addCallbacks(callbacks);
return startLoader(new Callbacks[] { callbacks });
}
}
private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
synchronized (mLock) {
// If there is already one running, tell it to stop.
//判断是否有LoaderTask在运行
boolean wasRunning = stopLoader();
boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
//有参数,取newCallbacks
boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
if (callbacksList.length > 0) {
// Clear any pending bind-runnables from the synchronized load process.
for (Callbacks cb : callbacksList) {
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
//已经加载过数据,直接进行绑定数据
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
loaderResults.bindWorkspace(bindAllCallbacks);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
loaderResults.bindAllApps();
loaderResults.bindDeepShortcuts();
loaderResults.bindWidgets();
return true;
} else {
stopLoader();
//LoaderTask实现了Runnable接口
mLoaderTask = new LoaderTask(
mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
//MODEL_EXECUTOR即LooperExecutor,内部有Handler,Handler接收的是HandlerThread的Looper对象
//在execute()通过Handler让LoaderTask在子线程运行
MODEL_EXECUTOR.post(mLoaderTask);
}
}
}
return false;
}
LoaderTask
public void run() {
synchronized (this) {
// Skip fast if we are already stopped.
//stopLocked()方法会把mStopped置为false
if (mStopped) {
return;
}
}
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
TimingLogger logger = new TimingLogger(TAG, "run");
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
Trace.beginSection("LoadWorkspace");
try {
//加载数据
loadWorkspace(allShortcuts, memoryLogger);
} finally {
Trace.endSection();
}
logASplit(logger, "loadWorkspace");
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
// sanitizeData should not be invoked if the workspace is loaded from a db different
// from the main db as defined in the invariant device profile.
// (e.g. both grid preview and minimal device mode uses a different db)
if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
verifyNotStopped();
sanitizeData();
logASplit(logger, "sanitizeData");
}
//mStopped为true,则抛出一个取消异常,中断往下执行
verifyNotStopped();
//应用图标数据绑定到控件上
mResults.bindWorkspace(true /* incrementBindId */);
logASplit(logger, "bindWorkspace");
mModelDelegate.workspaceLoadComplete();
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");
// Take a break
waitForIdle();
logASplit(logger, "step 1 complete");
verifyNotStopped();
// second step
Trace.beginSection("LoadAllApps");
List<LauncherActivityInfo> allActivityList;
try {
//集中应用列表的数据
allActivityList = loadAllApps();
} finally {
Trace.endSection();
}
logASplit(logger, "loadAllApps");
verifyNotStopped();
//绑定应用列表的数据
mResults.bindAllApps();
logASplit(logger, "bindAllApps");
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
logASplit(logger, "update icon cache");
verifyNotStopped();
logASplit(logger, "save shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
logASplit(logger, "step 2 complete");
verifyNotStopped();
// third step
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
logASplit(logger, "loadDeepShortcuts");
verifyNotStopped();
mResults.bindDeepShortcuts();
logASplit(logger, "bindDeepShortcuts");
verifyNotStopped();
logASplit(logger, "save deep shortcuts in icon cache");
updateHandler.updateIcons(allDeepShortcuts,
new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
waitForIdle();
logASplit(logger, "step 3 complete");
verifyNotStopped();
// fourth step
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
logASplit(logger, "load widgets");
verifyNotStopped();
mResults.bindWidgets();
logASplit(logger, "bindWidgets");
verifyNotStopped();
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
logASplit(logger, "save widgets in icon cache");
// fifth step
loadFolderNames();
verifyNotStopped();
updateHandler.finish();
logASplit(logger, "finish icon update");
mModelDelegate.modelLoadComplete();
transaction.commit();
memoryLogger.clearLogs();
} catch (CancellationException e) {
// Loader stopped, ignore
logASplit(logger, "Cancelled");
} catch (Exception e) {
memoryLogger.printLogs();
throw e;
} finally {
logger.dumpToLog();
}
TraceHelper.INSTANCE.endSection(traceToken);
}
private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
//CONTENT_URI为content://com.android.launcher3.settings/favorites
loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
null /* selection */, logger);
}
protected void loadWorkspace(
List<ShortcutInfo> allDeepShortcuts,
Uri contentUri,
String selection,
@Nullable LoaderMemoryLogger logger) {
...
if (clearDb) {
Log.d(TAG, "loadWorkspace: resetting launcher database");
//调到LauncherProvider的call方法中的METHOD_CREATE_EMPTY_DB逻辑
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
}
Log.d(TAG, "loadWorkspace: loading default favorites");
//调到LauncherProvider的call方法中的METHOD_LOAD_DEFAULT_FAVORITES逻辑
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
synchronized (mBgDataModel) {
mBgDataModel.clear();
mPendingPackages.clear();
...
//contentResolver.query(contentUri 查询的是favorites表的数据
//在构造方法通过游标获取一些列的索引
final LoaderCursor c = new LoaderCursor(
contentResolver.query(contentUri, null, selection, null, null), contentUri,
mApp, mUserManagerState);
//保存图标、图标快捷方式的数据类
WorkspaceItemInfo info;
//小部件相关的数据类
LauncherAppWidgetInfo appWidgetInfo;
//moveToNext会从表中获取一些数据
while (!mStopped && c.moveToNext()) {
...
switch (c.itemType) {
//应用图标的快捷方式
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
//应用图标
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
//长按图标,从应用信息拖出的快捷方式
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
...
if (c.restoreFlag != 0) {
// Already verified above that user is same as default user
info = c.getRestoredItemInfo(intent);
} else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
//构建应用图标的WorkspaceItemInfo
info = c.getAppShortcutInfo(
intent,
allowMissingTarget,
useLowResIcon,
!FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
} else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
if (unlockedUsers.get(c.serialNumber)) {
ShortcutInfo pinnedShortcut =
shortcutKeyToPinnedShortcuts.get(key);
if (pinnedShortcut == null) {
// The shortcut is no longer valid.
c.markDeleted("Pinned shortcut not found");
continue;
}
info = new WorkspaceItemInfo(pinnedShortcut, context);
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
}
intent = info.getIntent();
allDeepShortcuts.add(pinnedShortcut);
} else {
// Create a shortcut info in disabled mode for now.
info = c.loadSimpleWorkspaceItem();
info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
} else { // item type == ITEM_TYPE_SHORTCUT
//应用图标快捷方式的WorkspaceItemInfo
info = c.loadSimpleWorkspaceItem();
// Shortcuts are only available on the primary profile
if (!TextUtils.isEmpty(targetPkg)
&& pmHelper.isAppSuspended(targetPkg, c.user)) {
disabledState |= FLAG_DISABLED_SUSPENDED;
}
info.options = c.getInt(optionsIndex);
// App shortcuts that used to be automatically added to Launcher
// didn't always have the correct intent flags set, so do that
// here
if (intent.getAction() != null &&
intent.getCategories() != null &&
intent.getAction().equals(Intent.ACTION_MAIN) &&
intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
}
...
if (info != null) {
//WorkspaceItemInfo添加到mBgDataModel
c.checkAndAddItem(info, mBgDataModel, logger);
}
//文件夹
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
c.applyCommonProperties(folderInfo);
// Do not trim the folder label, as is was set by the user.
folderInfo.title = c.getString(c.titleIndex);
folderInfo.spanX = 1;
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
// no special handling required for restored folders
c.markRestored();
//也是添加到mBgDataModel中
c.checkAndAddItem(folderInfo, mBgDataModel, logger);
break;
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
...
c.checkAndAddItem(appWidgetInfo, mBgDataModel);
从数据库表中获取数据封装到对应的WorkspaceItemInfo、LauncherAppWidgetInfo、FolderInfo 数据类,然后添加到BgDataModel对象中
LoaderTask的run()方法中 mResults.bindWorkspace(true /* incrementBindId */)的逻辑
LoaderResults,继承BaseLoaderResults
public void bindWorkspace(boolean incrementBindId) {
// Save a copy of all the bg-thread collections
ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
final IntArray orderedScreenIds = new IntArray();
ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
//把BgDataModel的数据复制到新的集合中
synchronized (mBgDataModel) {
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
mBgDataModel.lastBindId++;
}
mMyBindingId = mBgDataModel.lastBindId;
}
//从Launcher的onCreate()方法入口,Launcher是实现了Callbacks 接口
for (Callbacks cb : mCallbacksList) {
new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
}
}
WorkspaceBinder,是BaseLoaderResults的内部类
private void bind() {
final IntSet currentScreenIds =
mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks);
// Separate the items that are on the current screen, and all the other remaining items
//要显示页的WorkspaceItems集合
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
//其他页的WorkspaceItems集合
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NULL_INT_SET, "bind (1) currentScreenIds: "
+ currentScreenIds
+ ", pointer: "
+ mCallbacks
+ ", name: "
+ mCallbacks.getClass().getName());
}
//过滤数据
filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NULL_INT_SET, "bind (2) currentScreenIds: "
+ currentScreenIds);
}
filterCurrentWorkspaceItems(currentScreenIds, mAppWidgets, currentAppWidgets,
otherAppWidgets);
final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
//切换到主线程执行Callbacks接口(Launcher实现了)的clearPendingBinds()和startBinding()方法
executeCallbacksTask(c -> {
c.clearPendingBinds();
//workspace会移除所有的页面,然后添加一个页面
c.startBinding();
}, mUiExecutor);
// Bind workspace screens
//workspace添加所有的页面
executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
// Load items on the current page.
//绑定要显示页的应用图标数据
bindWorkspaceItems(currentWorkspaceItems, mUiExecutor);
////绑定要显示页的小部件数据
bindAppWidgets(currentAppWidgets, mUiExecutor);
mExtraItems.forEach(item ->
executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
RunnableList pendingTasks = new RunnableList();
Executor pendingExecutor = pendingTasks::add;
//绑定其他页数据
bindWorkspaceItems(otherWorkspaceItems, pendingExecutor);
bindAppWidgets(otherAppWidgets, pendingExecutor);
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
pendingExecutor.execute(
() -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.resumeModelPush(FLAG_LOADER_RUNNING);
});
executeCallbacksTask(
c -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
c.onInitialBindComplete(currentScreenIds, pendingTasks);
}, mUiExecutor);
mCallbacks.bindStringCache(mBgDataModel.stringCache.clone());
}
private void bindWorkspaceItems(
final ArrayList<ItemInfo> workspaceItems, final Executor executor) {
// Bind the workspace items
int count = workspaceItems.size();
for (int i = 0; i < count; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i);
executeCallbacksTask(
//调用到Launcher的bindItems
c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
executor);
}
}
Launcher
public void bindItems(
final List<ItemInfo> items,
final boolean forceAnimateIcons,
final boolean focusFirstItemForAccessibility) {
// Get the list of added items and intersect them with the set of items here
final Collection<Animator> bounceAnims = new ArrayList<>();
boolean canAnimatePageChange = canAnimatePageChange();
Workspace<?> workspace = mWorkspace;
int newItemsScreenId = -1;
int end = items.size();
View newView = null;
//遍历ItemInfo集合,WorkspaceItemInfo 是ItemInfo的子类
for (int i = 0; i < end; i++) {
final ItemInfo item = items.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;
}
final View view;
//不同Item的类型处理
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
//构建控件,这里是BubbleTextView
view = createShortcut(info);
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item);
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
view = inflateAppWidget((LauncherAppWidgetInfo) item);
if (view == null) {
continue;
}
break;
}
default:
throw new RuntimeException("Invalid Item Type");
}
/*
* Remove colliding items.
*/
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);
if (v == null) {
Log.e(TAG, "bindItems failed when removing colliding item=" + item);
}
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
if (FeatureFlags.IS_STUDIO_BUILD) {
throw (new RuntimeException(desc));
} else {
getModelWriter().deleteItemFromDatabase(item, desc);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON,
TAG + "bindItems failed for item=" + item);
}
continue;
}
}
}
//添加到页面,也就是cellLayout中
workspace.addInScreenFromBind(view, item);
if (forceAnimateIcons) {
// Animate all the applications up now
view.setAlpha(0f);
view.setScaleX(0f);
view.setScaleY(0f);
bounceAnims.add(createNewAppBounceAnimation(view, i));
newItemsScreenId = item.screenId;
}
if (newView == null) {
newView = view;
}
}
View viewToFocus = newView;
// Animate to the correct pager
if (forceAnimateIcons && newItemsScreenId > -1) {
AnimatorSet anim = new AnimatorSet();
anim.playTogether(bounceAnims);
if (focusFirstItemForAccessibility && viewToFocus != null) {
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
});
}
int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
final Runnable startBounceAnimRunnable = anim::start;
if (canAnimatePageChange && newItemsScreenId != 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) {
closeOpenViews(false);
mWorkspace.snapToPage(newScreenIndex);
mWorkspace.postDelayed(startBounceAnimRunnable,
NEW_APPS_ANIMATION_DELAY);
}
}
}, NEW_APPS_PAGE_MOVE_DELAY);
} else {
mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
}
} else if (focusFirstItemForAccessibility && viewToFocus != null) {
viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
workspace.requestLayout();
}
View createShortcut(WorkspaceItemInfo info) {
// This can be called before PagedView#pageScrollsInitialized returns true, so use the
// first page, which we always assume to be present.
return createShortcut((ViewGroup) mWorkspace.getChildAt(0), info);
}
/**
* Creates a view representing a shortcut inflated from the specified resource.
*
* @param parent The group the shortcut belongs to. This is not necessarily the group where
* the shortcut should be added.
* @param info The data structure describing the shortcut.
* @return A View inflated from layoutResId.
*/
public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
//app_icon布局文件是DoubleShadowBubbleTextView
BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
//设置图标、名字
favorite.applyFromWorkspaceItem(info);
//设置点击事件
favorite.setOnClickListener(getItemOnClickListener());
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
BubbleTextView
@UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
applyIconAndLabel(info);
setItemInfo(info);
applyLoadingState(promiseStateChanged);
applyDotState(info, false /* animate */);
setDownloadStateContentDescription(info, info.getProgressLevel());
}
//图标
@UiThread
protected void applyIconAndLabel(ItemInfoWithIcon info) {
boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
|| mDisplay == DISPLAY_TASKBAR;
int flags = useTheme ? FLAG_THEMED : 0;
if (mHideBadge) {
flags |= FLAG_NO_BADGE;
}
FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
mDotParams.appColor = iconDrawable.getIconColor();
mDotParams.dotColor = getContext().getResources()
.getColor(android.R.color.system_accent3_200, getContext().getTheme());
setIcon(iconDrawable);
applyLabel(info);
}
//名字
@UiThread
private void applyLabel(ItemInfoWithIcon info) {
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.isDisabled()
? getContext().getString(R.string.disabled_app_label, info.contentDescription)
: info.contentDescription);
}
}
Workspace,实现了WorkspaceLayoutManager
WorkspaceLayoutManager
default void addInScreenFromBind(View child, ItemInfo info) {
int x = info.cellX;
int y = info.cellY;
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|| info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
int screenId = info.screenId;
x = getHotseat().getCellXFromOrder(screenId);
y = getHotseat().getCellYFromOrder(screenId);
}
addInScreen(child, info.container, info.screenId, x, y, info.spanX, info.spanY);
}
/**
* Adds the specified child in the specified screen. The position and dimension of
* the child are defined by x, y, spanX and spanY.
*
* @param child The child to add in one of the workspace's screens.
* @param screenId The screen in which to add the child.
* @param x The X position of the child in the screen's grid.
* @param y The Y position of the child in the screen's grid.
* @param spanX The number of cells spanned horizontally by the child.
* @param spanY The number of cells spanned vertically by the child.
*/
default void addInScreen(View child, int container, int screenId, int x, int y,
int spanX, int spanY) {
if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (getScreenWithId(screenId) == null) {
Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
// DEBUGGING - Print out the stack trace to see where we are adding from
new Throwable().printStackTrace();
return;
}
}
if (EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
// This should never happen
throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
}
final CellLayout layout;
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|| container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
layout = getHotseat();
// Hide folder title in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(false);
}
} else {
// Show folder title if not in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(true);
}
//获取对应的页面,也就是CellLayout
layout = getScreenWithId(screenId);
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
CellLayoutLayoutParams lp;
if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId);
} else {
lp = (CellLayoutLayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}
if (spanX < 0 && spanY < 0) {
lp.isLockedToGrid = false;
}
// Get the canonical child id to uniquely represent this view in this screen
ItemInfo info = (ItemInfo) child.getTag();
int childId = info.getViewId();
boolean markCellsAsOccupied = !(child instanceof Folder);
//添加到CellLayout
if (!layout.addViewToCellLayout(child, -1, childId, lp, markCellsAsOccupied)) {
// TODO: This branch occurs when the workspace is adding views
// outside of the defined grid
// maybe we should be deleting these items from the LauncherModel?
Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
}
child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
if (child instanceof DropTarget) {
onAddDropTarget((DropTarget) child);
}
}
Launcher通过LauncherModel去加载数据,LauncherModel判断是否已经加载过数据,如果没有,会创建一个LoaderTask对象,它实现了Runnable接口,然后交给线程池,在子线程中去加载数据
LoaderTask的run()方法调用loadWorkspace()方法从数据库中表favorites获取数据,根据不同的类型封装到不同的数据类中,比如应用图标是WorkspaceItemInfo,然后存储到BgDataModel的集合中
run()方法接着调用bindWorkspace()方法进行绑定, 会先进行数据过滤,绑定第一页的数据,再绑定其他页的数据,切换到主线程,调用Launcher的bindItems()方法进行绑定
bindItems()方法根据不同的Item类型创建不同的View,比如应用图标是DoubleShadowBubbleTextView控件,然后设置数据、点击事件,接着通过Workspace添加到页面中