1.配置文件
(1)
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher">
packege 属性可指定生成的gen源文件夹的包名,同时也表示程序运行时的进程名称
original-package 表示源码中真实的源代码层次结构
当 original-package和package相同时,声明Activity的时候可以使用.ClassName方式。否则需要使用完整的类名声明。当包名发生变更时,通过original-package来通知系统—-包名需要变更。旧包对应的数据文件也将重命名为original-package指定的包名。这样就能保证数据可延续使用。original-package只能用于system.img中包含的软件。
(2)
permission标签用来声明一个权限被其他程序声明访问的权限
Declares a security permission that can be used to limit access to specific components or features of this or other applications
(3)
Activity声明如上的意图过滤器可实现home的启动器功能,在按home键之后弹出对应的程序供用户选择
(4)
<application
android:name="com.android.launcher2.LauncherApplication"
……
该标签中 android:name属性配置的类会在程序运行时被初始化,即为代码中使用getApplication()方法获得的对象
2.Launcher启动流程
从清单文件中得知com.android.launcher2.Launcher为launcher的主要activity。
以及LauncherApplication继承了Application类,在程序初始化的时候需要先加载此类。
Launcher和普通activity一样,其生命周期如下
(1)
LauncherApplication.java
由程序初始化时启动,初始化部分设备的参数以及注册广播接收者等等
public void onCreate() {
super.onCreate();
// set sIsScreenXLarge and sScreenDensity *before* creating icon cache
//判断是否是大屏幕(最小边长>720px)
sIsScreenLarge = getResources().getBoolean(R.bool.is_large_screen);
//获取屏幕密度
sScreenDensity = getResources().getDisplayMetrics().density;
//新建icon缓存
mIconCache = new IconCache(this);
mModel = new LauncherModel(this, mIconCache);
//动态注册广播接收者(app的卸载,安装和改变)
// 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");
//动态注册广播接收者(app是否可用,地域变化,屏幕方向变化等等)
registerReceiver(mModel, filter);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
registerReceiver(mModel, filter);
//动态注册广播接收者(搜索提供者变化等等)
filter = new IntentFilter();
filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
registerReceiver(mModel, filter);
filter = new IntentFilter();
filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
registerReceiver(mModel, filter);
// Register for changes to the favorites
ContentResolver resolver = getContentResolver();
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
mFavoritesObserver);
}
(2)
Launcher.java
由程序初始化时启动,初始化view的布局以及启动后续的操作
@Override
protected void onCreate(Bundle savedInstanceState) {
……
super.onCreate(savedInstanceState);
//获取application对象
LauncherApplication app = ((LauncherApplication)getApplication());
mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(),
Context.MODE_PRIVATE);
//获取launchermodel对象,封装了数据库操作等
mModel = app.setLauncher(this);
//获取应用图标的缓存集合
mIconCache = app.getIconCache();
mDragController = new DragController(this);
mInflater = getLayoutInflater();
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
// this also ensures that any synchronous binding below doesn't re-trigger another
// LauncherModel load.
mPaused = false;
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing(
Environment.getExternalStorageDirectory() + "/launcher");
}
//校验本地设置是否更改
checkForLocaleChange();
setContentView(R.layout.launcher);
//初始化页面的各个控件
setupViews();
//控制是否显示Cling画面,仅在第一次使用(或清空数据)之后展现guide画面
showFirstRunWorkspaceCling();
//注册ContentResolver
registerContentObservers();
lockAllApps();
//恢复状态
mSavedState = savedInstanceState;
restoreState(mSavedState);
//恢复状态后更新widget
// Update customization drawer _after_ restoring the states
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.onPackagesUpdated();
}
if (PROFILE_STARTUP) {
android.os.Debug.stopMethodTracing();
}
if (!mRestoring) {
if (sPausedFromUserAction) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
//需要重新绑定所有worksapce页面
mModel.startLoader(true, -1);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
//需要重新绑定当前worksapce页面
mModel.startLoader(true, mWorkspace.getCurrentPage());
}
}
//校验allapps是否已加载
if (!mModel.isAllAppsLoaded()) {
ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();
mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);
}
// For handling default keys
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
//注册广播接收者
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mCloseSystemDialogsReceiver, filter);
//更新search图标,app市场图标等
updateGlobalIcons();
// On large interfaces, we want the screen to auto-rotate based on the current orientation
unlockScreenOrientation(true);
}
(3)
LauncherModel.java
实现加载workspace的celllayout和allapps的功能
public void startLoader(boolean isLaunching, int synchronousBindPage) {
synchronized (mLock) {
if (DEBUG_LOADERS) {
Log.d(TAG, "startLoader isLaunching=" + isLaunching);
}
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
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.
// also, don't downgrade isLaunching if we're already running
isLaunching = isLaunching || stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp, isLaunching);
//此时只是预加载某个页面
if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
//此时只是加载全部所有的celllayout和allapps
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}
当 synchronousBindPage为-1时,表示需要加载所有的页面(跳至步骤8),而synchronousBindPage>-1则是需要预加载某个页面(跳至步骤4)
(4)
LauncherModel.java
将加载分为加载workspace和加载allapps画面
void runBindSynchronousPage(int synchronousBindPage) {
……
synchronized (mLock) {
if (mIsLoaderTaskRunning) {
// Ensure that we are never running the background loading at this point since
// we also touch the background collections
throw new RuntimeException("Error! Background loading is already running");
}
}
// XXX: Throw an exception if we are already loading (since we touch the worker thread
// data structures, we can't allow any other thread to touch that data, but because
// this call is synchronous, we can get away with not locking).
// The LauncherModel is static in the LauncherApplication and mHandler may have queued
// operations from the previous activity. We need to ensure that all queued operations
// are executed before any synchronous binding work is done.
mHandler.flush();
// Divide the set of loaded items into those that we are binding synchronously, and
// everything else that is to be bound normally (asynchronously).
//异步绑定workspace页面,优先加载当前页面
bindWorkspace(synchronousBindPage);
// XXX: For now, continue posting the binding of AllApps as there are other issues that
// arise from that.
onlyBindAllApps();
}
(5)
LauncherModel.java
以下代码中callbacks即为Launcher的weakreference。launcher实现了callbacks的接口。当内存紧张的时候会被虚拟机回收此块内存。造成Launcher重启的现象(在widget的grid画面尤其明显,当快速旋转屏幕时,不停加载widgets造成内存使用过多,callbacks被gc。不使用weakreference则会导致oom报错)
实现绑定对应page的celllayout。 synchronizeBindPage>-1时异步加载。加载完成后依次调用callbacks的startBinding(),onPageBoundSynchronously(currentScreen),finishBindingItems().可以在launcher里实现相应的加载每一步之后的操作。
/**
* Binds all loaded data to actual views on the main thread.
*/
private void bindWorkspace(int synchronizeBindPage) {
final long t = SystemClock.uptimeMillis();
Runnable r;
// Don't use these two variables in any of the callback runnables.
// Otherwise we hold a reference to them.
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderTask running with no launcher");
return;
}
final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
oldCallbacks.getCurrentWorkspaceScreen();
// Load all the items that are on the current page first (and in the process, unbind
// all the existing workspace items before we call startBinding() below.
unbindWorkspaceItemsOnMainThread();
ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
ArrayList<LauncherAppWidgetInfo> appWidgets =
new ArrayList<LauncherAppWidgetInfo>();
HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
synchronized (sBgLock) {
workspaceItems.addAll(sBgWorkspaceItems);
appWidgets.addAll(sBgAppWidgets);
folders.putAll(sBgFolders);
itemsIdMap.putAll(sBgItemsIdMap);
}
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
new ArrayList<LauncherAppWidgetInfo>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
new ArrayList<LauncherAppWidgetInfo>();
HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
// Separate the items that are on the current screen, and all the other remaining items
//填充当前workspace的cellayout和hotseat的appItem
filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
//填充当前workspace的cellayout的wigdet
filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
otherAppWidgets);
//填充当前workspace的cellayout和hotseat的folder
filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
otherFolders);
sortWorkspaceItemsSpatially(currentWorkspaceItems);
sortWorkspaceItemsSpatially(otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
//此时准备向workspace画面添加item
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//weakreference当被gc的时候怎么通知?此时需要重启
//调用Launcher的startBinding
callbacks.startBinding();
}
}
};
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
// 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) {
callbacks.onPageBoundSynchronously(currentScreen);
}
}
};
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
// Load all the remaining pages (if we are loading synchronously, we want to defer this
// work until after the first render)
mDeferredBindRunnables.clear();
//加载其他页面(除当前页面)
bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
(/**如果加载的是全部页面则为false否则为true**/isLoadingSynchronously ? mDeferredBindRunnables : null));
// Tell the workspace that we're done binding items
//结束launcher的绑定item过程
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.finishBindingItems();
}
// If we're profiling, ensure this is the last thing in the queue.
if (DEBUG_LOADERS) {
Log.d(TAG, "bound workspace in "
+ (SystemClock.uptimeMillis()-t) + "ms");
}
mIsLoadingAndBindingWorkspace = false;
}
};
if (isLoadingSynchronously) {
mDeferredBindRunnables.add(r);
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
}
(6)
Launcher.java
开始准备绑定,清除掉所有celllayout的子view(其实是ShortcutAndWidgetContainer的子 view,ShortcutAndWidgetContainer是celllayout的唯一的子view)
正常来说 WorkSpace-》CellLayout-》ShortcutAndWidgetContainer-》ItemInfo
/**
* Refreshes the shortcuts shown on the workspace.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
//开始准备绑定items
final Workspace workspace = mWorkspace;
mNewShortcutAnimatePage = -1;
mNewShortcutAnimateViews.clear();
mWorkspace.clearDropTargets();
int count = workspace.getChildCount();
//移除所有的cellLayout的子view
for (int i = 0; i < count; i++) {
// Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
layoutParent.removeAllViewsInLayout();
}
mWidgetsToAdvance.clear();
if (mHotseat != null) {
mHotseat.resetLayout();
}
}
(7)
Launcher.java
callbacks最后一步,完成所有的绑定工作
/**
* Callback saying that there aren't any more items to bind.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void finishBindingItems() {
setLoadOnResume();
//binditem 完成
if (mSavedState != null) {
if (!mWorkspace.hasFocus()) {
mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
}
//所有状态已恢复?清空savestate
mSavedState = null;
}
mWorkspace.restoreInstanceStateForRemainingPages();
//当worksapce加载是用户添加item到worksapce则将其加入到sPendingAddList中,此时可以处理添加事件
// If we received the result of any pending adds while the loader was running (e.g. the
// widget configuration forced an orientation change), process them now.
for (int i = 0; i < sPendingAddList.size(); i++) {
completeAdd(sPendingAddList.get(i));
}
sPendingAddList.clear();
// Update the market app icon as necessary (the other icons will be managed in response to
// package changes in bindSearchablesChanged()
updateAppMarketIcon();
// Animate up any icons as necessary
if (mVisible || mWorkspaceLoading) {
Runnable newAppsRunnable = new Runnable() {
@Override
public void run() {
//新app初始动画?
runNewAppsAnimation(false);
}
};
boolean willSnapPage = mNewShortcutAnimatePage > -1 &&
mNewShortcutAnimatePage != mWorkspace.getCurrentPage();
if (canRunNewAppsAnimation()) {
// If the user has not interacted recently, then either snap to the new page to show
// the new-apps animation or just run them if they are to appear on the current page
if (willSnapPage) {
mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable);
} else {
runNewAppsAnimation(false);
}
} else {
// If the user has interacted recently, then just add the items in place if they
// are on another page (or just normally if they are added to the current page)
runNewAppsAnimation(willSnapPage);
}
}
mWorkspaceLoading = false;
}
(8)
LauncherModel.java
此时执行的是LoaderTask的run方法(实现了runnable接口),当前我显示的allapps画面则需要先加载allapps画面,再加载workspace画面。反之,则先加载workspace画面再加在allapps画面.
loadAndBindWorkspace()方法会调用步骤五的方法,其参数为-1,表示需要加载全部页面。
public void run() {
synchronized (mLock) {
mIsLoaderTaskRunning = true;
}
// 前台UI是allapps则先加载allapps画面,否则加载workspace
// 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).
final Callbacks cbk = mCallbacks.get();
//callback被回收了则默认使用加载workspace,否则按照allapps是否在前台来加载
final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
keep_running: {
// Elevate priority when Home launches for the first time to avoid
// starving at boot time. Staring at a blank home is not cool.
synchronized (mLock) {
if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
(mIsLaunching ? "DEFAULT" : "BACKGROUND"));
android.os.Process.setThreadPriority(mIsLaunching
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
}
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
//先加载workspace
loadAndBindWorkspace();
} else {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
//先加载allapps
loadAndBindAllApps();
}
if (mStopped) {
break keep_running;
}
……
waitForIdle();
// second step
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
} else {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
loadAndBindWorkspace();
}
// Restore the default thread priority after we are done loading items
synchronized (mLock) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
}
// Update the saved icons if necessary
if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
synchronized (sBgLock) {
for (Object key : sBgDbIconCache.keySet()) {
updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
}
sBgDbIconCache.clear();
}
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext = null;
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == this) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
}
}
(9)
LauncherModel.java
该方法需要在第一次被调用时通过调用 loadWorkspace方法加载对应资源目录下的default_workspace.xml文件。该文件描述
workspace里对应位置的app,widget等等(也包括hotseat),
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) {
return;
}
mWorkspaceLoaded = true;
}
}
// Bind the workspace
bindWorkspace(-1);
}
(10)
LauncherModel.java
//加载workspace
private void loadWorkspace() {
final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mContext;
final ContentResolver contentResolver = context.getContentResolver();
final PackageManager manager = context.getPackageManager();
final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
final boolean isSafeMode = manager.isSafeMode();
// Make sure the default workspace is loaded, if needed
// 判断是否准备加载默认的default_workspace.xml里
mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
synchronized (sBgLock) {
sBgWorkspaceItems.clear();
sBgAppWidgets.clear();
sBgFolders.clear();
sBgItemsIdMap.clear();
sBgDbIconCache.clear();
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
//查找需要展现在桌面上的app及widget
final Cursor c = contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
// +1 for the hotseat (it can be larger than the workspace)
// Load workspace in reverse order to ensure that latest items are loaded first (and
// before any earlier duplicates)
final ItemInfo occupied[][][] =
new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];
try {
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int intentIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.INTENT);
final int titleIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.TITLE);
final int iconTypeIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.ICON_TYPE);
final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
final int iconPackageIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.ICON_PACKAGE);
final int iconResourceIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.ICON_RESOURCE);
final int containerIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.CONTAINER);
final int itemTypeIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.ITEM_TYPE);
final int appWidgetIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_ID);
final int screenIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.SCREEN);
final int cellXIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.CELLY);
final int spanXIndex = c.getColumnIndexOrThrow
(LauncherSettings.Favorites.SPANX);
final int spanYIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.SPANY);
//final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
//final int displayModeIndex = c.getColumnIndexOrThrow(
// LauncherSettings.Favorites.DISPLAY_MODE);
ShortcutInfo info;
String intentDescription;
LauncherAppWidgetInfo appWidgetInfo;
int container;
long id;
Intent intent;
while (!mStopped && c.moveToNext()) {
try {
int itemType = c.getInt(itemTypeIndex);
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
intentDescription = c.getString(intentIndex);
try {
intent = Intent.parseUri(intentDescription, 0);
} catch (URISyntaxException e) {
continue;
}
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = getShortcutInfo(manager, intent, context, c, iconIndex,
titleIndex, mLabelCache);
} else {
info = getShortcutInfo(c, context, iconTypeIndex,
iconPackageIndex, iconResourceIndex, iconIndex,
titleIndex);
// 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) {
info.intent = intent;
info.id = c.getLong(idIndex);
container = c.getInt(containerIndex);
info.container = container;
info.screen = c.getInt(screenIndex);
info.cellX = c.getInt(cellXIndex);
info.cellY = c.getInt(cellYIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, info)) {
break;
}
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
sBgWorkspaceItems.add(info);
break;
default:
// Item is in a user folder
FolderInfo folderInfo =
findOrMakeFolder(sBgFolders, container);
folderInfo.add(info);
break;
}
sBgItemsIdMap.put(info.id, info);
// now that we've loaded everthing re-save it with the
// icon in case it disappears somehow.
queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
} else {
// Failed to load the shortcut, probably because the
// activity manager couldn't resolve it (maybe the app
// was uninstalled), or the db row was somehow screwed up.
// Delete it.
id = c.getLong(idIndex);
Log.e(TAG, "Error loading shortcut " + id + ", removing it");
contentResolver.delete(LauncherSettings.Favorites.getContentUri(
id, false), null, null);
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
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:
sBgWorkspaceItems.add(folderInfo);
break;
}
sBgItemsIdMap.put(folderInfo.id, folderInfo);
sBgFolders.put(folderInfo.id, folderInfo);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
// Read all Launcher-specific widget details
int appWidgetId = c.getInt(appWidgetIdIndex);
id = c.getLong(idIndex);
final AppWidgetProviderInfo provider =
widgets.getAppWidgetInfo(appWidgetId);
if (!isSafeMode && (provider == null || provider.provider == null ||
provider.provider.getPackageName() == null)) {
String log = "Deleting widget that isn't installed anymore: id="
+ id + " appWidgetId=" + appWidgetId;
Log.e(TAG, log);
Launcher.sDumpLogs.add(log);
itemsToRemove.add(id);
} else {
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
provider.provider);
appWidgetInfo.id = id;
appWidgetInfo.screen = c.getInt(screenIndex);
appWidgetInfo.cellX = c.getInt(cellXIndex);
appWidgetInfo.cellY = c.getInt(cellYIndex);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
appWidgetInfo.minSpanX = minSpan[0];
appWidgetInfo.minSpanY = minSpan[1];
container = c.getInt(containerIndex);
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Log.e(TAG, "Widget found where container != " +
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
continue;
}
appWidgetInfo.container = c.getInt(containerIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, appWidgetInfo)) {
break;
}
sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
sBgAppWidgets.add(appWidgetInfo);
}
break;
}
} catch (Exception e) {
Log.w(TAG, "Desktop items loading interrupted:", e);
}
}
} finally {
c.close();
}
if (itemsToRemove.size() > 0) {
ContentProviderClient client = contentResolver.acquireContentProviderClient(
LauncherSettings.Favorites.CONTENT_URI);
// Remove dead items
for (long id : itemsToRemove) {
if (DEBUG_LOADERS) {
Log.d(TAG, "Removed id = " + id);
}
// Don't notify content observers
try {
client.delete(LauncherSettings.Favorites.getContentUri(id, false),
null, null);
} catch (RemoteException e) {
Log.w(TAG, "Could not remove id = " + id);
}
}
}
if (DEBUG_LOADERS) {
Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Log.d(TAG, "workspace layout: ");
for (int y = 0; y < mCellCountY; y++) {
String line = "";
for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
if (s > 0) {
line += " | ";
}
for (int x = 0; x < mCellCountX; x++) {
line += ((occupied[s][x][y] != null) ? "#" : ".");
}
}
Log.d(TAG, "[ " + line + " ]");
}
}
}
}
(11)
WorkSpace.java
当launcher.xml被setContentView时,workspace和hotseat等类也需要初始化。
workspace构造函数如下
/**
* Used to inflate the Workspace from XML.
*
* @param context The application's context.
* @param attrs The attributes set containing the Workspace's customization values.
* @param defStyle Unused.
*/
public Workspace(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContentIsRefreshable = false;
mOriginalPageSpacing = mPageSpacing;
mDragEnforcer = new DropTarget.DragEnforcer(context);
// With workspace, data is available straight from the get-go
setDataIsReady();
mLauncher = (Launcher) context;
final Resources res = getResources();
mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
mFadeInAdjacentScreens = false;
mWallpaperManager = WallpaperManager.getInstance(context);
int cellCountX = DEFAULT_CELL_COUNT_X;
int cellCountY = DEFAULT_CELL_COUNT_Y;
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.Workspace, defStyle, 0);
if (LauncherApplication.isScreenLarge()) {
// 判断是否是大屏幕(最小边长大于720px)
// Determine number of rows/columns dynamically
// TODO: This code currently fails on tablets with an aspect ratio < 1.3.
// Around that ratio we should make cells the same size in portrait and
// landscape
TypedArray actionBarSizeTypedArray =
context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
Point minDims = new Point();
Point maxDims = new Point();
mLauncher.getWindowManager().getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
//手动设置的时候会覆盖此值,是否可以先判断值是否设定?
cellCountX = 1;
while (CellLayout.widthInPortrait(res, cellCountX + 1) <= minDims.x) {
cellCountX++;
}
cellCountY = 1;
while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
<= minDims.y) {
cellCountY++;
}
}
mSpringLoadedShrinkFactor =
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
mSpringLoadedPageSpacing =
res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing);
mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
// if the value is manually specified, use that instead
cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
a.recycle();
setOnHierarchyChangeListener(this);
//更新cell的尺寸
LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
//打开触摸反馈
setHapticFeedbackEnabled(false);
//初始化workspace,包括壁纸的设定等等
initWorkspace();
// Disable multitouch across the workspace/all apps/customize tray
setMotionEventSplittingEnabled(true);
// Unless otherwise specified this view is important for accessibility.
if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
}
(12)
WorkSpace.java
初始化workspace的部分属性,包括壁纸的设定等等
/**
* Initializes various states for this workspace.
*/
protected void initWorkspace() {
Context context = getContext();
mCurrentPage = mDefaultPage;
//当前页设置为默认页
Launcher.setScreen(mCurrentPage);
LauncherApplication app = (LauncherApplication)context.getApplicationContext();
//保存应用图片的缓存
mIconCache = app.getIconCache();
//显示workspace的边界,通常来说viewgroup是不需要描绘边界的
setWillNotDraw(false);
//设置子view绘图缓存开启
setChildrenDrawnWithCacheEnabled(true);
final Resources res = getResources();
try {
mBackground = res.getDrawable(R.drawable.apps_customize_bg);
} catch (Resources.NotFoundException e) {
// In this case, we will skip drawing background protection
}
//wallPaper 偏移
mWallpaperOffset = new WallpaperOffsetInterpolator();
Display display = mLauncher.getWindowManager().getDefaultDisplay();
//获取屏幕大小,此方法在android 4.0之前不支持
display.getSize(mDisplaySize);
mWallpaperTravelWidth = (int) (mDisplaySize.x *
wallpaperTravelToScreenWidthRatio(mDisplaySize.x, mDisplaySize.y));
mMaxDistanceForFolderCreation = (0.55f * res.getDimensionPixelSize(R.dimen.app_icon_size));
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
}