【Launcher2源码解读】Launcher中的数据模型
Launcher中显示的App,Shortcut,Folder,Widget都是数据模型
继承关系图:
看一下ItemInfo.java
重要属性:X、Y坐标,占用位置(用于Widget),标题等
重要方法:初始构造,数据库存储,bitmap在db的读和存
class ItemInfo { static final int NO_ID = -1; //数据库中的Id long id = NO_ID; //Item类型,app,shortcut,folder,appwidget int itemType; //用来区分item在workspace还是在dock,还是在文件夹中 long container = NO_ID; //在第几个屏幕上 int screen = -1; //横坐标 int cellX = -1; //从坐标 int cellY = -1; //横向占用位置 int spanX = 1; //纵向占用位置 int spanY = 1; /** * Indicates the minimum X cell span. */ int minSpanX = 1; /** * Indicates the minimum Y cell span. */ int minSpanY = 1; //是否需要更新 boolean requiresDbUpdate = false; //标题文本 CharSequence title; //拖拽位置 int[] dropPos = null; ItemInfo() { } ItemInfo(ItemInfo info) { id = info.id; cellX = info.cellX; cellY = info.cellY; spanX = info.spanX; spanY = info.spanY; screen = info.screen; itemType = info.itemType; container = info.container; // tempdebug: LauncherModel.checkItemInfo(this); } /** Returns the package name that the intent will resolve to, or an empty string if * none exists. */ static String getPackageName(Intent intent) { if (intent != null) { String packageName = intent.getPackage(); if (packageName == null && intent.getComponent() != null) { packageName = intent.getComponent().getPackageName(); } if (packageName != null) { return packageName; } } return ""; } /** * Write the fields of this item to the DB * * @param values */ void onAddToDatabase(ContentValues values) { values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType); values.put(LauncherSettings.Favorites.CONTAINER, container); values.put(LauncherSettings.Favorites.SCREEN, screen); values.put(LauncherSettings.Favorites.CELLX, cellX); values.put(LauncherSettings.Favorites.CELLY, cellY); values.put(LauncherSettings.Favorites.SPANX, spanX); values.put(LauncherSettings.Favorites.SPANY, spanY); } void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) { values.put(LauncherSettings.Favorites.CELLX, cellX); values.put(LauncherSettings.Favorites.CELLY, cellY); } static byte[] flattenBitmap(Bitmap bitmap) { // Try go guesstimate how much space the icon will take when serialized // to avoid unnecessary allocations/copies during the write. int size = bitmap.getWidth() * bitmap.getHeight() * 4; ByteArrayOutputStream out = new ByteArrayOutputStream(size); try { bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); out.flush(); out.close(); return out.toByteArray(); } catch (IOException e) { Log.w("Favorite", "Could not write icon"); return null; } } static void writeBitmap(ContentValues values, Bitmap bitmap) { if (bitmap != null) { byte[] data = flattenBitmap(bitmap); values.put(LauncherSettings.Favorites.ICON, data); } } /** * It is very important that sub-classes implement this if they contain any references * to the activity (anything in the view hierarchy etc.). If not, leaks can result since * ItemInfo objects persist across rotation and can hence leak by holding stale references * to the old view hierarchy / activity. */ void unbind() { } @Override public String toString() { return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container + " screen=" + screen + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + dropPos + ")"; }
ApplicationInfo.java
继承ItemInfo.java 增加了Intent属性,ComponentName,PackageName,StartActivity等属性和方法。
class ApplicationInfo extends ItemInfo { private static final String TAG = "Launcher2.ApplicationInfo"; /** * The intent used to start the application. */ Intent intent; /** * A bitmap version of the application icon. */ Bitmap iconBitmap; /** * The time at which the app was first installed. */ long firstInstallTime; ComponentName componentName; static final int DOWNLOADED_FLAG = 1; static final int UPDATED_SYSTEM_APP_FLAG = 2; int flags = 0; ApplicationInfo() { itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT; } /** * Must not hold the Context. */ public ApplicationInfo(PackageManager pm, ResolveInfo info, IconCache iconCache, HashMap<Object, CharSequence> labelCache) { final String packageName = info.activityInfo.applicationInfo.packageName; this.componentName = new ComponentName(packageName, info.activityInfo.name); this.container = ItemInfo.NO_ID; this.setActivity(componentName, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); try { int appFlags = pm.getApplicationInfo(packageName, 0).flags; if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) { flags |= DOWNLOADED_FLAG; if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { flags |= UPDATED_SYSTEM_APP_FLAG; } } firstInstallTime = pm.getPackageInfo(packageName, 0).firstInstallTime; } catch (NameNotFoundException e) { Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName); } iconCache.getTitleAndIcon(this, info, labelCache); } public ApplicationInfo(ApplicationInfo info) { super(info); componentName = info.componentName; title = info.title.toString(); intent = new Intent(info.intent); flags = info.flags; firstInstallTime = info.firstInstallTime; } /** Returns the package name that the shortcut's intent will resolve to, or an empty string if * none exists. */ String getPackageName() { return super.getPackageName(intent); } /** * Creates the application intent based on a component name and various launch flags. * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}. * * @param className the class name of the component representing the intent * @param launchFlags the launch flags */ final void setActivity(ComponentName className, int launchFlags) { intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setComponent(className); intent.setFlags(launchFlags); itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION; } @Override public String toString() { return "ApplicationInfo(title=" + title.toString() + ")"; } public static void dumpApplicationInfoList(String tag, String label, ArrayList<ApplicationInfo> list) { Log.d(tag, label + " size=" + list.size()); for (ApplicationInfo info: list) { Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap + " firstInstallTime=" + info.firstInstallTime); } } public ShortcutInfo makeShortcut() { return new ShortcutInfo(this); } }
剩下的都是类似的,继承ItemInfo,再根据具体的需求添加属性和方法。
FolderInfo.item
class FolderInfo extends ItemInfo { /** * Whether this folder has been opened */ boolean opened; /** * The apps and shortcuts */ ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>(); ArrayList<FolderListener> listeners = new ArrayList<FolderListener>(); FolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; } /** * Add an app or shortcut * * @param item */ public void add(ShortcutInfo item) { contents.add(item); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onAdd(item); } itemsChanged(); } /** * Remove an app or shortcut. Does not change the DB. * * @param item */ public void remove(ShortcutInfo item) { contents.remove(item); for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onRemove(item); } itemsChanged(); } public void setTitle(CharSequence title) { this.title = title; for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onTitleChanged(title); } } @Override void onAddToDatabase(ContentValues values) { super.onAddToDatabase(values); values.put(LauncherSettings.Favorites.TITLE, title.toString()); } void addListener(FolderListener listener) { listeners.add(listener); } void removeListener(FolderListener listener) { if (listeners.contains(listener)) { listeners.remove(listener); } } void itemsChanged() { for (int i = 0; i < listeners.size(); i++) { listeners.get(i).onItemsChanged(); } } @Override void unbind() { super.unbind(); listeners.clear(); } interface FolderListener { public void onAdd(ShortcutInfo item); public void onRemove(ShortcutInfo item); public void onTitleChanged(CharSequence title); public void onItemsChanged(); } }
LauncherAppWidgetInfo.java
class LauncherAppWidgetInfo extends ItemInfo { /** * Indicates that the widget hasn't been instantiated yet. */ static final int NO_ID = -1; /** * Identifier for this widget when talking with * {@link android.appwidget.AppWidgetManager} for updates. */ int appWidgetId = NO_ID; ComponentName providerName; // TODO: Are these necessary here? int minWidth = -1; int minHeight = -1; private boolean mHasNotifiedInitialWidgetSizeChanged; /** * View that holds this widget after it's been created. This view isn't created * until Launcher knows it's needed. */ AppWidgetHostView hostView = null; LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; this.appWidgetId = appWidgetId; this.providerName = providerName; // Since the widget isn't instantiated yet, we don't know these values. Set them to -1 // to indicate that they should be calculated based on the layout and minWidth/minHeight spanX = -1; spanY = -1; } @Override void onAddToDatabase(ContentValues values) { super.onAddToDatabase(values); values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId); } /** * When we bind the widget, we should notify the widget that the size has changed if we have not * done so already (only really for default workspace widgets). */ void onBindAppWidget(Launcher launcher) { if (!mHasNotifiedInitialWidgetSizeChanged) { notifyWidgetSizeChanged(launcher); } } /** * Trigger an update callback to the widget to notify it that its size has changed. */ void notifyWidgetSizeChanged(Launcher launcher) { AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY); mHasNotifiedInitialWidgetSizeChanged = true; } @Override public String toString() { return "AppWidget(id=" + Integer.toString(appWidgetId) + ")"; } @Override void unbind() { super.unbind(); hostView = null; } }
PendingAddInfo,PendingAddShortcutInfo,PendingAddWidgetInfo
class PendingAddItemInfo extends ItemInfo { /** * The component that will be created. */ ComponentName componentName; } class PendingAddShortcutInfo extends PendingAddItemInfo { ActivityInfo shortcutActivityInfo; public PendingAddShortcutInfo(ActivityInfo activityInfo) { shortcutActivityInfo = activityInfo; } @Override public String toString() { return "Shortcut: " + shortcutActivityInfo.packageName; } } class PendingAddWidgetInfo extends PendingAddItemInfo { int minWidth; int minHeight; int minResizeWidth; int minResizeHeight; int previewImage; int icon; AppWidgetProviderInfo info; AppWidgetHostView boundWidget; Bundle bindOptions = null; // Any configuration data that we want to pass to a configuration activity when // starting up a widget String mimeType; Parcelable configurationData; public PendingAddWidgetInfo(AppWidgetProviderInfo i, String dataMimeType, Parcelable data) { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; this.info = i; componentName = i.provider; minWidth = i.minWidth; minHeight = i.minHeight; minResizeWidth = i.minResizeWidth; minResizeHeight = i.minResizeHeight; previewImage = i.previewImage; icon = i.icon; if (dataMimeType != null && data != null) { mimeType = dataMimeType; configurationData = data; } } // Copy constructor public PendingAddWidgetInfo(PendingAddWidgetInfo copy) { minWidth = copy.minWidth; minHeight = copy.minHeight; minResizeWidth = copy.minResizeWidth; minResizeHeight = copy.minResizeHeight; previewImage = copy.previewImage; icon = copy.icon; info = copy.info; boundWidget = copy.boundWidget; mimeType = copy.mimeType; configurationData = copy.configurationData; componentName = copy.componentName; itemType = copy.itemType; spanX = copy.spanX; spanY = copy.spanY; minSpanX = copy.minSpanX; minSpanY = copy.minSpanY; bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone(); } @Override public String toString() { return "Widget: " + componentName.toShortString(); } }