Android Launcher3源码分析与修改

Launcher和Setting是客户需求经常改动的地方,不过其代码量也不容小觑。今天就初略来看一下,以下内容都是本人查阅资料加上自己的理解得出,由于自己水平有限,如果误导还请指出:

先从AndroidManifest文件入手,Launcher3的工程名是ToggleWeightWatcher,包名是com.android.launcher3。

关于权限,Launcher3的权限有permission和uses-permission两种。

permission是自定义的权限,uses-permission是调用系统的permission。其中自定义permission有几个属性:

android:permissionGroup 可选,为Permission进行分组,可以由以下常量定义:

ACCOUNTS:  账户管理相关

COST_MONEY:让用户花钱但不需要通过与他们直接牵涉

DEVELOPMENT_TOOLS:开发相关

HARDWARE_CONTROLS:直接访问硬件设备

LOCATION:  访问用户当前位置

MESSAGE:  信息相关

NETWORK:  访问网络服务相关

PERSONAL_INFO:访问用户私人数据相关

PHONE_CALLS:拨号相关

STORAGE:SD卡相关

SYSTEM_TOOLS:系统API有关

android:protectionLevel必有,

normal:低风险权限,只要申请了就可以使用(在AndroidManifest.xml中添加标签),安装时不需要用户确认;
dangerous:高风险权限,安装时需要用户的确认才可使用;
signature:只有当申请权限的应用程序的数字签名与声明此权限的应用程序的数字签名相同时(如果是申请系统权限,则需要与系统签名相同),才能将权限授给它;
signatureOrSystem:签名相同,或者申请权限的应用为系统应用(在system image中)

其他的android:label, android:name, android:description是描述性信息

[html]  view plain copy
  1. <permission  
  2.        android:name="com.android.launcher3.permission.PRELOAD_WORKSPACE"  
  3.        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"  
  4.        android:protectionLevel="system|signature" />  
  5.    <permission  
  6.        android:name="com.android.launcher.permission.INSTALL_SHORTCUT"  
  7.        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"  
  8.        android:protectionLevel="dangerous"  
  9.        android:label="@string/permlab_install_shortcut"  
  10.        android:description="@string/permdesc_install_shortcut" />  
  11.    <permission  
  12.        android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"  
  13.        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"  
  14.        android:protectionLevel="dangerous"  
  15.        android:label="@string/permlab_uninstall_shortcut"  
  16.        android:description="@string/permdesc_uninstall_shortcut"/>  
  17.    <permission  
  18.        android:name="com.android.launcher3.permission.READ_SETTINGS"  
  19.        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"  
  20.        android:protectionLevel="normal"  
  21.        android:label="@string/permlab_read_settings"  
  22.        android:description="@string/permdesc_read_settings"/>  
  23.    <permission  
  24.        android:name="com.android.launcher3.permission.WRITE_SETTINGS"  
  25.        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"  
  26.        android:protectionLevel="normal"  
  27.        android:label="@string/permlab_write_settings"  
  28.        android:description="@string/permdesc_write_settings"/>  
  29.   
  30.    <permission  
  31.        android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"  
  32.        android:protectionLevel="signature"  
  33.        />  
  34.   
  35.    <uses-permission android:name="android.permission.CALL_PHONE" />  
  36.    <uses-permission android:name="android.permission.SET_WALLPAPER" />  
  37.    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />  
  38.    <uses-permission android:name="android.permission.VIBRATE" />  
  39.    <uses-permission android:name="android.permission.BIND_APPWIDGET" />  
  40.    <uses-permission android:name="android.permission.GET_ACCOUNTS" />  
  41.    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  
  42.    <uses-permission android:name="android.permission.READ_MEDIA_STORAGE" />  
  43.    <uses-permission android:name="android.permission.ADVANCED_WIDGET_API"/>  
  44.    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />  
  45.    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />  
  46.    <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />  
  47.    <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />  
  48.    <uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />  
  49.      
  50.    <uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>  

下面逐一看一下各个权限:

PRELOAD_WORKSPACE

INSTALL_SHORTCUT / UNINSTALL_SHORTCUT 创建 / 删除快捷方式

READ_SETTINGS / WRITE_SETTINGS 读取 / 写入设置配置

RECEIVE_LAUNCH_BROADCASTS 接收启动广播

CALL_PHONE 拨打电话

SET_WALLPAPER 设置壁纸

SET_WALLPAPER_HINTS  设置壁纸(桌面)提示,目前不懂这个什么用,是初次启动Launcher的使用提示吗?

VIBRATE 震动

BIND_APPWIDGET 访问AppWidget数据

GET_ACCOUNTS 访问账号服务的账号列表

READ_EXTERNAL_STORAGE 读取外部存储

READ_MEDIA_STORAGE 读取媒体存储

ADVANCED_WIDGET_API    高级Widget接口

GET_PACKAGE_SIZE  获取应用包大小


接着看一下AndroidManifest文件中的部分:

这里面声明的都是显式Activity,Service,Provider之类的。

先上源码:

[html]  view plain copy
  1. <application  
  2.       android:name="com.android.launcher3.LauncherApplication"  
  3.       android:label="@string/application_name"  
  4.       android:icon="@mipmap/ic_launcher_home"  
  5.       android:hardwareAccelerated="true"  
  6.       android:largeHeap="@bool/config_largeHeap"  
  7.       android:supportsRtl="true">  
  8.       <activity  
  9.           android:name="com.android.launcher3.Launcher"  
  10.           android:launchMode="singleTask"  
  11.           android:clearTaskOnLaunch="true"  
  12.           android:stateNotNeeded="true"  
  13.           android:theme="@style/Theme"  
  14.           android:windowSoftInputMode="adjustPan"  
  15.           android:screenOrientation="nosensor">  
  16.           <intent-filter>  
  17.               <action android:name="android.intent.action.MAIN" />  
  18.               <category android:name="android.intent.category.HOME" />  
  19.               <category android:name="android.intent.category.DEFAULT" />  
  20.               <category android:name="android.intent.category.MONKEY"/>  
  21.           intent-filter>  
  22.       activity>  
  23.   
  24.         
  25.       <activity android:name="com.android.launcher3.HideAppsActivity"  
  26.           android:label="@string/hideapps"  
  27.           android:icon="@drawable/ic_launcher_home"  
  28.           android:screenOrientation="nosensor" >  
  29.           <intent-filter>  
  30.               <category android:name="android.intent.category.DEFAULT" />  
  31.           intent-filter>  
  32.       activity>  
  33.   
  34.       <activity  
  35.           android:name="com.android.launcher3.ToggleWeightWatcher"  
  36.           android:label="@string/toggle_weight_watcher"  
  37.           android:enabled="@bool/debug_memory_enabled"  
  38.           android:icon="@mipmap/ic_launcher_home">  
  39.           <intent-filter>  
  40.               <action android:name="android.intent.action.MAIN" />  
  41.               <category android:name="android.intent.category.DEFAULT" />  
  42.               <category android:name="android.intent.category.LAUNCHER" />  
  43.           intent-filter>  
  44.       activity>  
  45.   
  46.       <activity  
  47.           android:name="com.android.launcher3.WallpaperPickerActivity"  
  48.           android:theme="@style/Theme.WallpaperCropper"  
  49.           android:label="@string/pick_wallpaper"  
  50.           android:icon="@mipmap/ic_launcher_wallpaper"  
  51.           android:finishOnCloseSystemDialogs="true"  
  52.           android:process=":wallpaper_chooser"  
  53.    android:configChanges="orientation|keyboard|keyboardHidden|screenSize">  
  54.           <intent-filter>  
  55.               <action android:name="android.intent.action.SET_WALLPAPER" />  
  56.               <category android:name="android.intent.category.DEFAULT" />  
  57.           intent-filter>  
  58.       activity>  
  59.   
  60.       <activity  
  61.           android:name="com.android.launcher3.WallpaperCropActivity"  
  62.           android:theme="@style/Theme.WallpaperCropper"  
  63.           android:label="@string/crop_wallpaper"  
  64.           android:icon="@mipmap/ic_launcher_wallpaper"  
  65.           android:finishOnCloseSystemDialogs="true"  
  66.           android:process=":wallpaper_chooser">  
  67.           <intent-filter>  
  68.               <action android:name="android.service.wallpaper.CROP_AND_SET_WALLPAPER" />  
  69.               <category android:name="android.intent.category.DEFAULT" />  
  70.               <data android:mimeType="image/*" />  
  71.           intent-filter>  
  72.       activity>  
  73.   
  74.         
  75.       <activity  
  76.           android:name="com.android.launcher3.MemoryDumpActivity"  
  77.           android:theme="@android:style/Theme.NoDisplay"  
  78.           android:label="@string/debug_memory_activity"  
  79.           android:enabled="@bool/debug_memory_enabled"  
  80.           android:excludeFromRecents="true"  
  81.           android:icon="@mipmap/ic_launcher_home"  
  82.           >  
  83.           <intent-filter>  
  84.               <action android:name="android.intent.action.MAIN" />  
  85.               <category android:name="android.intent.category.DEFAULT" />  
  86.               <category android:name="android.intent.category.LAUNCHER" />  
  87.           intent-filter>  
  88.       activity>  
  89.   
  90.       <service android:name="com.android.launcher3.MemoryTracker"  
  91.           android:enabled="@bool/debug_memory_enabled"  
  92.           >  
  93.       service>  
  94.   
  95.         
  96.       <receiver  
  97.           android:name="com.android.launcher3.PreloadReceiver"  
  98.           android:permission="com.android.launcher3.permission.PRELOAD_WORKSPACE">  
  99.           <intent-filter>  
  100.               <action android:name="com.android.launcher3.action.PRELOAD_WORKSPACE" />  
  101.           intent-filter>  
  102.       receiver>  
  103.   
  104.         
  105.       <receiver  
  106.           android:name="com.android.launcher3.InstallShortcutReceiver"  
  107.           android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">  
  108.           <intent-filter>  
  109.               <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />  
  110.           intent-filter>  
  111.       receiver>  
  112.   
  113.         
  114.       <receiver  
  115.           android:name="com.android.launcher3.UninstallShortcutReceiver"  
  116.           android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">  
  117.           <intent-filter>  
  118.               <action android:name="com.android.launcher.action.UNINSTALL_SHORTCUT" />  
  119.           intent-filter>  
  120.       receiver>  
  121.   
  122.         
  123.       <receiver  
  124.           android:name="com.android.launcher3.UserInitializeReceiver"  
  125.           android:exported="false">  
  126.           <intent-filter>  
  127.               <action android:name="android.intent.action.USER_INITIALIZE" />  
  128.           intent-filter>  
  129.       receiver>  
  130.   
  131.       <receiver android:name="com.android.launcher3.PackageChangedReceiver" >  
  132.           <intent-filter>  
  133.               <action android:name="android.intent.action.PACKAGE_CHANGED"/>  
  134.               <action android:name="android.intent.action.PACKAGE_REPLACED"/>  
  135.               <action android:name="android.intent.action.PACKAGE_REMOVED"/>  
  136.               <data android:scheme="package">data>  
  137.           intent-filter>  
  138.       receiver>  
  139.   
  140.         
  141.       <provider  
  142.           android:name="com.android.launcher3.LauncherProvider"  
  143.           android:authorities="com.android.launcher3.settings"  
  144.           android:exported="true"  
  145.           android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS"  
  146.           android:readPermission="com.android.launcher3.permission.READ_SETTINGS" />  
  147.   
  148.       <meta-data android:name="android.nfc.disable_beam_default"  
  149.                      android:value="true" />  
  150.   application>  


下面先分析一下各个Activity的大致功能,后期将进行验证:

com.android.launcher3.Launcher Activity,主Activity

com.android.launcher3.ToggleWeightWatcher  Activity,启动Activity

com.android.launcher3.HideAppsActivity Activity,隐藏应用抽屉程序清单中应用的Activity。

com.android.launcher3.WallpaperPickerActivity Activity,选择壁纸的Activity

com.android.launcher3.WallpaperCropActivity  Activity,裁剪壁纸的Activity

com.android.launcher3.MemoryDumpActivity  Activity,这个是调试用的,可忽略

com.android.launcher3.MemoryTracker Service,调试用

com.android.launcher3.PreloadReceiver Receiver,用来响应预加载Launcher默认工作空间

com.android.launcher3.InstallShortcutReceiver  Receiver,用来响应添加其他应用的快捷方式

com.android.launcher3.UninstallShortcutReceiver Receiver,和上一个相反,用来响应去除其他应用的快捷方式

com.android.launcher3.UserInitializeReceiver  Receiver,用来响应新用户初始化,设置初始壁纸

com.android.launcher3.PackageChangedReceiver Receiver, 用来响应包变化

com.android.launcher3.LauncherProvider Provider,包括主页屏的数据,比如图标和AppWidget的位置、大小等。



AllAppList

这是一个比较核心的类,所有应用的信息都在这儿了。

下面看一下AllAppList类的成员变量和成员函数:

data是所有应用的清单:

[java]  view plain copy
  1. public ArrayList data =  
  2.            new ArrayList(DEFAULT_APPLICATIONS_NUMBER);  

added是自上次 更新(notify()广播)后新 增加的应用清单:

[java]  view plain copy
  1. public ArrayList added =  
  2.         new ArrayList(DEFAULT_APPLICATIONS_NUMBER);  


removed是自上次更新以来,所移除的应用清单:
[java]  view plain copy
  1. public ArrayList removed = new ArrayList();  


modified是自上次更新以来,所修改的应用清单:

[java]  view plain copy
  1. public ArrayList modified = new ArrayList();  


mIconCache 图标缓存

mAppFilter 应用过滤器

[java]  view plain copy
  1. private IconCache mIconCache;  
  2.   
  3. private AppFilter mAppFilter;  


新添加一个应用及其信息到list中,如果已存在则不添加:

[java]  view plain copy
  1. public void add(AppInfo info) {  
  2.        if (LauncherLog.DEBUG) {  
  3.            LauncherLog.d(TAG, "Add application in app list: app = " + info.componentName  
  4.                    + ", title = " + info.title);  
  5.        }  
  6.   
  7.        if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) {  
  8.            return;  
  9.        }  
  10.        if (findActivity(data, info.componentName)) {  
  11.            LauncherLog.i(TAG, "Application " + info + " already exists in app list, app = " + info);  
  12.            return;  
  13.        }  
  14.        data.add(info);  
  15.        added.add(info);  
  16.    }  

清空列表中的应用信息,包括data,added,removed,modified:
[java]  view plain copy
  1. public void clear() {  
  2.     if (LauncherLog.DEBUG) {  
  3.         LauncherLog.d(TAG, "clear all data in app list: app size = " + data.size());  
  4.     }  
  5.   
  6.     data.clear();  
  7.     // TODO: do we clear these too?  
  8.     added.clear();  
  9.     removed.clear();  
  10.     modified.clear();  
  11. }  


为指定包名添加Icon信息到matches 表单:
[java]  view plain copy
  1. public void addPackage(Context context, String packageName) {  
  2.     final List matches = findActivitiesForPackage(context, packageName);  
  3.   
  4.     if (LauncherLog.DEBUG) {  
  5.         LauncherLog.d(TAG, "addPackage: packageName = " + packageName + ", matches = " + matches.size());  
  6.     }  
  7.   
  8.     if (matches.size() > 0) {  
  9.         for (ResolveInfo info : matches) {  
  10.             add(new AppInfo(context.getPackageManager(), info, mIconCache, null));  
  11.         }  
  12.     }  
  13. }  


从data表单中删除指定包名的应用的包信息:
[java]  view plain copy
  1. public void removePackage(String packageName) {  
  2.      final List data = this.data;  
  3.      if (LauncherLog.DEBUG) {  
  4.          LauncherLog.d(TAG, "removePackage: packageName = " + packageName + ", data size = " + data.size());  
  5.      }  
  6.   
  7.      for (int i = data.size() - 1; i >= 0; i--) {  
  8.          AppInfo info = data.get(i);  
  9.          final ComponentName component = info.intent.getComponent();  
  10.          if (packageName.equals(component.getPackageName())) {  
  11.              removed.add(info);  
  12.              data.remove(i);  
  13.          }  
  14.      }  

更新包信息,若不存在,直接添加应用信息;若已存在,则先删除旧信息,然后添加新信息。
[java]  view plain copy
  1. public void updatePackage(Context context, String packageName) {  
  2.     final List matches = findActivitiesForPackage(context, packageName);  
  3.     if (LauncherLog.DEBUG) {  
  4.         LauncherLog.d(TAG, "updatePackage: packageName = " + packageName + ", matches = " + matches.size());  
  5.     }  
  6.   
  7.     if (matches.size() > 0) {  
  8.         // Find disabled/removed activities and remove them from data and add them  
  9.         // to the removed list.  
  10.         for (int i = data.size() - 1; i >= 0; i--) {  
  11.             final AppInfo applicationInfo = data.get(i);  
  12.             final ComponentName component = applicationInfo.intent.getComponent();  
  13.             if (packageName.equals(component.getPackageName())) {  
  14.                 if (!findActivity(matches, component)) {  
  15.                     removed.add(applicationInfo);  
  16.                     mIconCache.remove(component);  
  17.                     data.remove(i);  
  18.                 }  
  19.             }  
  20.         }  
  21.   
  22.         // Find enabled activities and add them to the adapter  
  23.         // Also updates existing activities with new labels/icons  
  24.         int count = matches.size();  
  25.         for (int i = 0; i < count; i++) {  
  26.             final ResolveInfo info = matches.get(i);  
  27.             final String pkgName = info.activityInfo.applicationInfo.packageName;  
  28.             final String className = info.activityInfo.name;  
  29.   
  30.             AppInfo applicationInfo = findApplicationInfoLocked(  
  31.                     info.activityInfo.applicationInfo.packageName,  
  32.                     info.activityInfo.name);  
  33.             if (applicationInfo == null) {  
  34.                 add(new AppInfo(context.getPackageManager(), info, mIconCache, null));  
  35.             } else {  
  36.                 mIconCache.remove(applicationInfo.componentName);  
  37.                 mIconCache.getTitleAndIcon(applicationInfo, info, null);  
  38.                 modified.add(applicationInfo);  
  39.             }  
  40.         }  
  41.     } else {  
  42.         // Remove all data for this package.  
  43.         for (int i = data.size() - 1; i >= 0; i--) {  
  44.             final AppInfo applicationInfo = data.get(i);  
  45.             final ComponentName component = applicationInfo.intent.getComponent();  
  46.             if (packageName.equals(component.getPackageName())) {  
  47.                 if (LauncherLog.DEBUG) {  
  48.                     LauncherLog.d(TAG, "Remove application from launcher: component = " + component);  
  49.                 }  
  50.                 removed.add(applicationInfo);  
  51.                 mIconCache.remove(component);  
  52.                 data.remove(i);  
  53.             }  
  54.         }  
  55.     }  
  56. }  


供MAIN/LAUNCHER通过包名packageName查询包管理器:
[java]  view plain copy
  1. static List findActivitiesForPackage(Context context, String packageName) {  
  2.     final PackageManager packageManager = context.getPackageManager();  
  3.   
  4.     final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  5.     mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  6.     mainIntent.setPackage(packageName);  
  7.   
  8.     final List apps = packageManager.queryIntentActivities(mainIntent, 0);  
  9.     return apps != null ? apps : new ArrayList();  
  10. }  


根据component是否找到对应的应用:
[java]  view plain copy
  1. /** 
  2.      * Returns whether apps contains component. 
  3.      */  
  4.     private static boolean findActivity(List apps, ComponentName component) {  
  5.         final String className = component.getClassName();  
  6.         for (ResolveInfo info : apps) {  
  7.             final ActivityInfo activityInfo = info.activityInfo;  
  8.             if (activityInfo.name.equals(className)) {  
  9.                 return true;  
  10.             }  
  11.         }  
  12.         return false;  
  13.     }  
  14.   
  15.     /** 
  16.      * Returns whether apps contains component. 
  17.      */  
  18.     private static boolean findActivity(ArrayList apps, ComponentName component) {  
  19.         final int N = apps.size();  
  20.         for (int i=0; i
  21.             final AppInfo info = apps.get(i);  
  22.             if (info.componentName.equals(component)) {  
  23.                 return true;  
  24.             }  
  25.         }  
  26.         return false;  
  27.     }  

根据指定的包名packageName,类明className返回一个Component对象:
[java]  view plain copy
  1. private AppInfo findApplicationInfoLocked(String packageName, String className) {  
  2.     for (AppInfo info: data) {  
  3.         final ComponentName component = info.intent.getComponent();  
  4.         if (packageName.equals(component.getPackageName())  
  5.                 && className.equals(component.getClassName())) {  
  6.             return info;  
  7.         }  
  8.     }  
  9.     return null;  
  10. }  


从default_toppackage.xml文件中加载默认顶部应用,加载成功返回true:
[java]  view plain copy
  1. static boolean loadTopPackage(final Context context) {  
  2.     boolean bRet = false;  
  3.     if (sTopPackages != null) {  
  4.         return bRet;  
  5.     }  
  6.   
  7.     sTopPackages = new ArrayList();  
  8.   
  9.     try {  
  10.         XmlResourceParser parser = context.getResources().getXml(R.xml.default_toppackage);  
  11.         AttributeSet attrs = Xml.asAttributeSet(parser);  
  12.         XmlUtils.beginDocument(parser, TAG_TOPPACKAGES);  
  13.   
  14.         final int depth = parser.getDepth();  
  15.   
  16.         int type = -1;  
  17.         while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)  
  18.                 && type != XmlPullParser.END_DOCUMENT) {  
  19.   
  20.             if (type != XmlPullParser.START_TAG) {  
  21.                 continue;  
  22.             }  
  23.   
  24.             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TopPackage);  
  25.   
  26.             sTopPackages.add(new TopPackage(a.getString(R.styleable.TopPackage_topPackageName),  
  27.                     a.getString(R.styleable.TopPackage_topClassName), a.getInt(  
  28.                             R.styleable.TopPackage_topOrder, 0)));  
  29.   
  30.             LauncherLog.d(TAG, "loadTopPackage: packageName = "  
  31.                     + a.getString(R.styleable.TopPackage_topPackageName)  
  32.                     + ", className = "  
  33.                     + a.getString(R.styleable.TopPackage_topClassName));  
  34.   
  35.             a.recycle();  
  36.         }  
  37.     } catch (XmlPullParserException e) {  
  38.         LauncherLog.w(TAG, "Got XmlPullParserException while parsing toppackage.", e);  
  39.     } catch (IOException e) {  
  40.         LauncherLog.w(TAG, "Got IOException while parsing toppackage.", e);  
  41.     }  
  42.   
  43.     return bRet;  
  44. }  


根据应用信息appInfo匹配默认顶部应用的对应应用,返回该应用下标:

[java]  view plain copy
  1. static int getTopPackageIndex(final AppInfo appInfo) {  
  2.     int retIndex = -1;  
  3.     if (sTopPackages == null || sTopPackages.isEmpty() || appInfo == null) {  
  4.         return retIndex;  
  5.     }  
  6.   
  7.     for (TopPackage tp : sTopPackages) {  
  8.         if (appInfo.componentName.getPackageName().equals(tp.packageName)  
  9.                 && appInfo.componentName.getClassName().equals(tp.className)) {  
  10.             retIndex = tp.order;  
  11.             break;  
  12.         }  
  13.     }  
  14.   
  15.     return retIndex;  
  16. }  


重排默认顶部应用所有应用的下标:
[java]  view plain copy
  1. void reorderApplist() {  
  2.     final long sortTime = DEBUG_LOADERS_REORDER ? SystemClock.uptimeMillis() : 0;  
  3.   
  4.     if (sTopPackages == null || sTopPackages.isEmpty()) {  
  5.         return;  
  6.     }  
  7.     ensureTopPackageOrdered();  
  8.   
  9.     final ArrayList dataReorder = new ArrayList(  
  10.             DEFAULT_APPLICATIONS_NUMBER);  
  11.   
  12.     for (TopPackage tp : sTopPackages) {  
  13.         int loop = 0;  
  14.         for (AppInfo ai : added) {  
  15.             if (DEBUG_LOADERS_REORDER) {  
  16.                 LauncherLog.d(TAG, "reorderApplist: remove loop = " + loop);  
  17.             }  
  18.   
  19.             if (ai.componentName.getPackageName().equals(tp.packageName)  
  20.                     && ai.componentName.getClassName().equals(tp.className)) {  
  21.                 if (DEBUG_LOADERS_REORDER) {  
  22.                     LauncherLog.d(TAG, "reorderApplist: remove packageName = "  
  23.                             + ai.componentName.getPackageName());  
  24.                 }  
  25.                 data.remove(ai);  
  26.                 dataReorder.add(ai);  
  27.                 dumpData();  
  28.                 break;  
  29.             }  
  30.             loop++;  
  31.         }  
  32.     }  
  33.   
  34.     for (TopPackage tp : sTopPackages) {  
  35.         int loop = 0;  
  36.         int newIndex = 0;  
  37.         for (AppInfo ai : dataReorder) {  
  38.             if (DEBUG_LOADERS_REORDER) {  
  39.                 LauncherLog.d(TAG, "reorderApplist: added loop = " + loop + ", packageName = "  
  40.                         + ai.componentName.getPackageName());  
  41.             }  
  42.   
  43.             if (ai.componentName.getPackageName().equals(tp.packageName)  
  44.                     && ai.componentName.getClassName().equals(tp.className)) {  
  45.                 newIndex = Math.min(Math.max(tp.order, 0), added.size());  
  46.                 if (DEBUG_LOADERS_REORDER) {  
  47.                     LauncherLog.d(TAG, "reorderApplist: added newIndex = " + newIndex);  
  48.                 }  
  49.                 /// M: make sure the array list not out of bound  
  50.                 if (newIndex < data.size()) {  
  51.                     data.add(newIndex, ai);  
  52.                 } else {  
  53.                     data.add(ai);  
  54.                 }  
  55.                 dumpData();  
  56.                 break;  
  57.             }  
  58.             loop++;  
  59.         }  
  60.     }  
  61.   
  62.     if (added.size() == data.size()) {  
  63.         added = (ArrayList) data.clone();  
  64.         LauncherLog.d(TAG, "reorderApplist added.size() == data.size()");  
  65.     }  
  66.   
  67.     if (DEBUG_LOADERS_REORDER) {  
  68.         LauncherLog.d(TAG, "sort and reorder took " + (SystemClock.uptimeMillis() - sortTime) + "ms");  
  69.     }  
  70. }  


Dump掉data列表中的应用信息:

[java]  view plain copy
  1. void dumpData() {  
  2.     int loop2 = 0;  
  3.     for (AppInfo ai : data) {  
  4.         if (DEBUG_LOADERS_REORDER) {  
  5.             LauncherLog.d(TAG, "reorderApplist data loop2 = " + loop2);  
  6.             LauncherLog.d(TAG, "reorderApplist data packageName = "  
  7.                     + ai.componentName.getPackageName());  
  8.         }  
  9.         loop2++;  
  10.     }  
  11. }  

确保top_package.xml中条目有序,以防特殊情况下top_package.xml会使数组列表越界:
[java]  view plain copy
  1. static void ensureTopPackageOrdered() {  
  2.     ArrayList tpOrderList = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);  
  3.     boolean bFirst = true;  
  4.     for (TopPackage tp : sTopPackages) {  
  5.         if (bFirst) {  
  6.             tpOrderList.add(tp);  
  7.             bFirst = false;  
  8.         } else {  
  9.             for (int i = tpOrderList.size() - 1; i >= 0; i--) {  
  10.                 TopPackage tpItor = tpOrderList.get(i);  
  11.                 if (0 == i) {  
  12.                     if (tp.order < tpOrderList.get(0).order) {  
  13.                         tpOrderList.add(0, tp);  
  14.                     } else {  
  15.                         tpOrderList.add(1, tp);  
  16.                     }  
  17.                     break;  
  18.                 }  
  19.   
  20.                 if ((tp.order < tpOrderList.get(i).order)  
  21.                     && (tp.order >= tpOrderList.get(i - 1).order)) {  
  22.                     tpOrderList.add(i, tp);  
  23.                     break;  
  24.                 } else if (tp.order > tpOrderList.get(i).order) {  
  25.                     tpOrderList.add(i + 1, tp);  
  26.                     break;  
  27.                 }  
  28.             }  
  29.         }  
  30.     }  
  31.   
  32.     if (sTopPackages.size() == tpOrderList.size()) {  
  33.         sTopPackages = (ArrayList) tpOrderList.clone();  
  34.         tpOrderList = null;  
  35.         LauncherLog.d(TAG, "ensureTopPackageOrdered done");  
  36.     } else {  
  37.         LauncherLog.d(TAG, "some mistake may occur when ensureTopPackageOrdered");  
  38.     }  
  39. }  


添加应用信息到add列表,注意不是added列表:
[java]  view plain copy
  1. public void addApp(final AppInfo info) {  
  2.     if (LauncherLog.DEBUG_EDIT) {  
  3.         LauncherLog.d(TAG, "Add application to data list: app = " + info.componentName);  
  4.     }  
  5.   
  6.     if (findActivity(data, info.componentName)) {  
  7.         LauncherLog.i(TAG, "The app " + info + " is already exist in data list.");  
  8.         return;  
  9.     }  
  10.     data.add(info);  
  11. }  

AppInfo

这个类也很关键,不过相对AllAppList来讲要简单很多,从代码量上就可以看出来。主要是App的详细信息,包括图标,初次安装时间之类的,下面细看AppInfo的成员变量和函数:

图标:

[java]  view plain copy
  1. Bitmap iconBitmap;  

应用的初次安装时间:

[java]  view plain copy
  1. long firstInstallTime;  


应用图标是否显示,true为显示。

[java]  view plain copy
  1. boolean isVisible = true;  

无参初始化,为应用创建快捷方式:
[java]  view plain copy
  1. AppInfo() {  
  2.        itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;  
  3.    }  


得到应用的图标和标题:

[java]  view plain copy
  1. public AppInfo(PackageManager pm, ResolveInfo info, IconCache iconCache,  
  2.           HashMap labelCache) {  
  3.       final String packageName = info.activityInfo.applicationInfo.packageName;  
  4.   
  5.       this.componentName = new ComponentName(packageName, info.activityInfo.name);  
  6.       this.container = ItemInfo.NO_ID;  
  7.       this.setActivity(componentName,  
  8.               Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);  
  9.   
  10.       this.setFlagAndInstallTime(pm, packageName);  
  11.       iconCache.getTitleAndIcon(this, info, labelCache);  
  12.   }  


初始化标志信息,指明应用是系统应用,还是用户下载:
[java]  view plain copy
  1. public static int initFlags(PackageInfo pi) {  
  2.     int appFlags = pi.applicationInfo.flags;  
  3.     int flags = 0;  
  4.     if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {  
  5.         flags |= DOWNLOADED_FLAG;  
  6.   
  7.         if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {  
  8.             flags |= UPDATED_SYSTEM_APP_FLAG;  
  9.         }  
  10.     }  
  11.     return flags;  
  12. }  


AppInfp的成员接口,可以获取组件名componentName,标题title,intent,标志flags,初次安装时间firstInstallTime等信息:
[java]  view plain copy
  1. public AppInfo(AppInfo info) {  
  2.     super(info);  
  3.     componentName = info.componentName;  
  4.     title = info.title.toString();  
  5.     intent = new Intent(info.intent);  
  6.     flags = info.flags;  
  7.     firstInstallTime = info.firstInstallTime;  
  8.   
  9.     /// M: copy value from source applcation info.  
  10.     pos = info.pos;  
  11.     isVisible = info.isVisible;  
  12.     stateChanged = info.stateChanged;  
  13. }  


根据类名和launchFlags创建应用Intent:
[java]  view plain copy
  1. final void setActivity(ComponentName className, int launchFlags) {  
  2.     intent = new Intent(Intent.ACTION_MAIN);  
  3.     intent.addCategory(Intent.CATEGORY_LAUNCHER);  
  4.     intent.setComponent(className);  
  5.     intent.setFlags(launchFlags);  
  6.     itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;  
  7. }  


设置标志,记录初次安装时间:
[java]  view plain copy
  1. public void setFlagAndInstallTime(final PackageManager pm, String packageName) {  
  2.     try {  
  3.         PackageInfo pi = pm.getPackageInfo(packageName, 0);  
  4.         flags = initFlags(pi);  
  5.         firstInstallTime = initFirstInstallTime(pi);  
  6.     } catch (NameNotFoundException e) {  
  7.         Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);  
  8.     }  
  9. }  
[java]  view plain copy
  1. public void setFlagAndInstallTime(final PackageManager pm) {  
  2.     String packageName = getPackageName();  
  3.     setFlagAndInstallTime(pm, packageName);  
  4. }  

返回快捷方式要启动应用的包名,如不存在则返回一个空字符串:
[java]  view plain copy
  1. String getPackageName() {  
  2.     String packageName = "";  
  3.     if (intent != null) {  
  4.         packageName = intent.getPackage();  
  5.         if (packageName == null && intent.getComponent() != null) {  
  6.             packageName = intent.getComponent().getPackageName();  
  7.         }          
  8.     }  
  9.     return packageName;  
  10. }  




今天就先这两个类吧,已经不短了。不过源码真心长,真心多,真心眼花缭乱,慢慢啃吧。



今天来看一下CropView和DynamicGrid两部分。

CropView是设置壁纸时通过缩放手势截取图片,DynamicGrid则是根据设备来指定图标显示的行列,下面细看一下:

CropView

为了便于查看,吸取前两篇的教训,直接把注释写在代码里,成员变量:

[java]  view plain copy
  1. private ScaleGestureDetector mScaleGestureDetector;  
  2. private long mTouchDownTime;  
  3. private float mFirstX, mFirstY;"white-space:pre"// 初始坐标位置  
  4. private float mLastX, mLastY;"white-space:pre">   // 结束坐标位置  
  5. private float mCenterX, mCenterY;"white-space:pre">   // 中间坐标位置  
  6. private float mMinScale;  
  7. private boolean mTouchEnabled = true;  
  8. private RectF mTempEdges = new RectF();  
  9. private float[] mTempPoint = new float[] { 00 };  
  10. private float[] mTempCoef = new float[] { 00 };  
  11. private float[] mTempAdjustment = new float[] { 00 };  
  12. private float[] mTempImageDims = new float[] { 00 };  
  13. private float[] mTempRendererCenter = new float[] { 00 };  
  14. TouchCallback mTouchCallback;  
  15. Matrix mRotateMatrix;  
  16. Matrix mInverseRotateMatrix;  

成员函数:

CropView的构造函数:

[java]  view plain copy
  1. public CropView(Context context) {  
  2.     this(context, null);  
  3. }  
  4.   
  5. public CropView(Context context, AttributeSet attrs) {  
  6.     super(context, attrs);  
  7.     mScaleGestureDetector = new ScaleGestureDetector(context, this);  
  8.     mRotateMatrix = new Matrix();  
  9.     mInverseRotateMatrix = new Matrix();  
  10. }  

得到图像的分辨率:

[java]  view plain copy
  1. private float[] getImageDims() {  
  2.     final float imageWidth = mRenderer.source.getImageWidth();"white-space:pre">  // 图像宽度  
  3.     final float imageHeight = mRenderer.source.getImageHeight();"white-space:pre">    // 图像高度  
  4.     float[] imageDims = mTempImageDims;  
  5.     imageDims[0] = imageWidth;  
  6.     imageDims[1] = imageHeight;  
  7.     mRotateMatrix.mapPoints(imageDims);  
  8.     imageDims[0] = Math.abs(imageDims[0]);  
  9.     imageDims[1] = Math.abs(imageDims[1]);  
  10.     return imageDims;  
  11. }  


得到上下左右的边界:
[java]  view plain copy
  1. private void getEdgesHelper(RectF edgesOut) {  
  2.     final float width = getWidth();  
  3.     final float height = getHeight();  
  4.     final float[] imageDims = getImageDims();  
  5.     final float imageWidth = imageDims[0];  
  6.     final float imageHeight = imageDims[1];  
  7.   
  8.     float initialCenterX = mRenderer.source.getImageWidth() / 2f;  
  9.     float initialCenterY = mRenderer.source.getImageHeight() / 2f;  
  10.   
  11.     float[] rendererCenter = mTempRendererCenter;  
  12.     rendererCenter[0] = mCenterX - initialCenterX;  
  13.     rendererCenter[1] = mCenterY - initialCenterY;  
  14.     mRotateMatrix.mapPoints(rendererCenter);  
  15.     rendererCenter[0] += imageWidth / 2;  
  16.     rendererCenter[1] += imageHeight / 2;  
  17.   
  18.     final float scale = mRenderer.scale;  
  19.     float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f)  
  20.             * scale + width / 2f;  
  21.     float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f)  
  22.             * scale + height / 2f;  
  23.     float leftEdge = centerX - imageWidth / 2f * scale;  
  24.     float rightEdge = centerX + imageWidth / 2f * scale;  
  25.     float topEdge = centerY - imageHeight / 2f * scale;  
  26.     float bottomEdge = centerY + imageHeight / 2f * scale;  
  27.   
  28.     edgesOut.left = leftEdge;  
  29.     edgesOut.right = rightEdge;  
  30.     edgesOut.top = topEdge;  
  31.     edgesOut.bottom = bottomEdge;  
  32. }  


得到截取的形状:
[java]  view plain copy
  1. public RectF getCrop() {  
  2.     final RectF edges = mTempEdges;  
  3.     getEdgesHelper(edges);  
  4.     final float scale = mRenderer.scale;  
  5.   
  6.     float cropLeft = -edges.left / scale;  
  7.     float cropTop = -edges.top / scale;  
  8.     float cropRight = cropLeft + getWidth() / scale;  
  9.     float cropBottom = cropTop + getHeight() / scale;  
  10.   
  11.     return new RectF(cropLeft, cropTop, cropRight, cropBottom);  
  12. }  

设置TileSource,根据TileSource更新Scale:

[java]  view plain copy
  1. public void setTileSource(TileSource source, Runnable isReadyCallback) {  
  2.         super.setTileSource(source, isReadyCallback);  
  3.         mCenterX = mRenderer.centerX;  
  4.         mCenterY = mRenderer.centerY;  
  5.         mRotateMatrix.reset();  
  6.         mRotateMatrix.setRotate(mRenderer.rotation);  
  7.         mInverseRotateMatrix.reset();  
  8.         mInverseRotateMatrix.setRotate(-mRenderer.rotation);  
  9.         updateMinScale(getWidth(), getHeight(), source, true);  
  10.     }  


宽和高改变后,重新设置Scale:

[java]  view plain copy
  1. protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  2.         updateMinScale(w, h, mRenderer.source, false);  
  3.     }  
  4.   
  5.     public void setScale(float scale) {  
  6.         synchronized (mLock) {  
  7.             mRenderer.scale = scale;  
  8.         }  
  9.     }  
  10.   
  11.     private void updateMinScale(int w, int h, TileSource source,  
  12.             boolean resetScale) {  
  13.         synchronized (mLock) {  
  14.             if (resetScale) {  
  15.                 mRenderer.scale = 1;  
  16.             }  
  17.             if (source != null) {  
  18.                 final float[] imageDims = getImageDims();  
  19.                 final float imageWidth = imageDims[0];  
  20.                 final float imageHeight = imageDims[1];  
  21.                 mMinScale = Math.max(w / imageWidth, h / imageHeight);  
  22.                 mRenderer.scale = Math.max(mMinScale, mRenderer.scale);  
  23.             }  
  24.         }  
  25.     }  

核心的来了,触摸响应:

[java]  view plain copy
  1. public boolean onTouchEvent(MotionEvent event) {  
  2.         int action = event.getActionMasked();  
  3.         final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;  
  4.         final int skipIndex = pointerUp ? event.getActionIndex() : -1;  
  5.   
  6.         // Determine focal point  
  7.         float sumX = 0, sumY = 0;  
  8.         final int count = event.getPointerCount();  
  9.         for (int i = 0; i < count; i++) {  
  10.             if (skipIndex == i)  
  11.                 continue;  
  12.             sumX += event.getX(i);  
  13.             sumY += event.getY(i);  
  14.         }  
  15.         final int div = pointerUp ? count - 1 : count;  
  16.         float x = sumX / div;  
  17.         float y = sumY / div;  
  18.   
  19.         if (action == MotionEvent.ACTION_DOWN) {"white-space:pre">    // 响应按下事件  
  20.             mFirstX = x;  
  21.             mFirstY = y;  
  22.             mTouchDownTime = System.currentTimeMillis();  
  23.             if (mTouchCallback != null) {  
  24.                 mTouchCallback.onTouchDown();  
  25.             }  
  26.         } else if (action == MotionEvent.ACTION_UP) {"white-space:pre">   // 响应抬起事件  
  27.             ViewConfiguration config = ViewConfiguration.get(getContext());  
  28.   
  29.             float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y)  
  30.                     * (mFirstY - y);  
  31.             float slop = config.getScaledTouchSlop()  
  32.                     * config.getScaledTouchSlop();  
  33.             long now = System.currentTimeMillis();  
  34.             if (mTouchCallback != null) {  
  35.                 // only do this if it's a small movement  
  36.                 if (squaredDist < slop  
  37.                         && now < mTouchDownTime  
  38.                                 + ViewConfiguration.getTapTimeout()) {  
  39.                     mTouchCallback.onTap();  
  40.                 }  
  41.                 mTouchCallback.onTouchUp();  
  42.             }  
  43.         }  
  44.   
  45.         if (!mTouchEnabled) {  
  46.             return true;  
  47.         }  
  48.   
  49.         synchronized (mLock) {"white-space:pre">      // 同步锁  
  50.             mScaleGestureDetector.onTouchEvent(event);"white-space:pre">  // 手势缩放  
  51.             switch (action) {  
  52.             case MotionEvent.ACTION_MOVE:  
  53.                 float[] point = mTempPoint;  
  54.                 point[0] = (mLastX - x) / mRenderer.scale;  
  55.                 point[1] = (mLastY - y) / mRenderer.scale;  
  56.                 mInverseRotateMatrix.mapPoints(point);  
  57.                 mCenterX += point[0];  
  58.                 mCenterY += point[1];  
  59.                 updateCenter();"white-space:pre">     // 更新中心点  
  60.                 invalidate();  
  61.                 break;  
  62.             }  
  63.             if (mRenderer.source != null) {  
  64.                 // Adjust position so that the wallpaper covers the entire area  
  65.                 // of the screen  
  66.                 final RectF edges = mTempEdges;  
  67.                 getEdgesHelper(edges);  
  68.                 final float scale = mRenderer.scale;  
  69.   
  70.                 float[] coef = mTempCoef;  
  71.                 coef[0] = 1;  
  72.                 coef[1] = 1;  
  73.                 mRotateMatrix.mapPoints(coef);  
  74.                 float[] adjustment = mTempAdjustment;  
  75.                 mTempAdjustment[0] = 0;  
  76.                 mTempAdjustment[1] = 0;  
  77.                 if (edges.left > 0) {  
  78.                     adjustment[0] = edges.left / scale;  
  79.                 } else if (edges.right < getWidth()) {  
  80.                     adjustment[0] = (edges.right - getWidth()) / scale;  
  81.                 }  
  82.                 if (edges.top > 0) {  
  83.                     adjustment[1] = FloatMath.ceil(edges.top / scale);  
  84.                 } else if (edges.bottom < getHeight()) {  
  85.                     adjustment[1] = (edges.bottom - getHeight()) / scale;  
  86.                 }  
  87.                 for (int dim = 0; dim <= 1; dim++) {  
  88.                     if (coef[dim] > 0)  
  89.                         adjustment[dim] = FloatMath.ceil(adjustment[dim]);  
  90.                 }  
  91.   
  92.                 mInverseRotateMatrix.mapPoints(adjustment);  
  93.                 mCenterX += adjustment[0];  
  94.                 mCenterY += adjustment[1];  
  95.                 updateCenter();  
  96.             }  
  97.         }  
  98.   
  99.         mLastX = x;  
  100.         mLastY = y;  
  101.         return true;  
  102.     }  





DynamicGrid

动态网格?先看两个核心类:

查询获取设备信息,宽和高以及分辨率:

[java]  view plain copy
  1. class DeviceProfileQuery {  
  2.     float widthDps;  
  3.     float heightDps;  
  4.     float value;  
  5.     PointF dimens;  
  6.   
  7.     DeviceProfileQuery(float w, float h, float v) {  
  8.         widthDps = w;  
  9.         heightDps = h;  
  10.         value = v;  
  11.         dimens = new PointF(w, h);  
  12.     }  
  13. }  

下面这个就长了,洋洋洒洒几百行:

[java]  view plain copy
  1. class DeviceProfile {  
  2.     String name;  
  3.     float minWidthDps;  
  4.     float minHeightDps;  
  5.     float numRows;  
  6.     float numColumns;  
  7.     float iconSize;  
  8.     float iconTextSize;  
  9.     float numHotseatIcons;  
  10.     float hotseatIconSize;  
  11.   
  12.     boolean isLandscape;  
  13.     boolean isTablet;  
  14.     boolean isLargeTablet;  
  15.     boolean transposeLayoutWithOrientation;  
  16.   
  17.     int desiredWorkspaceLeftRightMarginPx;  
  18.     int edgeMarginPx;  
  19.     Rect defaultWidgetPadding;  
  20.   
  21.     int widthPx;  
  22.     int heightPx;  
  23.     int availableWidthPx;  
  24.     int availableHeightPx;  
  25.     int iconSizePx;"white-space:pre">     // 图标大小  
  26.     int iconTextSizePx;"white-space:pre">     // 文字大小  
  27.     int cellWidthPx;  
  28.     int cellHeightPx;  
  29.     int folderBackgroundOffset;"white-space:pre"// 文件夹背景  
  30.     int folderIconSizePx;"white-space:pre">   // 文件夹图标像素大小  
  31.     int folderCellWidthPx;  
  32.     int folderCellHeightPx;  
  33.     int hotseatCellWidthPx;"white-space:pre"// 固定热键宽度  
  34.     int hotseatCellHeightPx;"white-space:pre">    // 固定热键高度  
  35.     int hotseatIconSizePx;  
  36.     int hotseatBarHeightPx;"white-space:pre"// 固定热键上端的分割线  
  37.     int hotseatAllAppsRank;  
  38.     int allAppsNumRows;"white-space:pre">     // 应用行数  
  39.     int allAppsNumCols;"white-space:pre">     // 应用列数  
  40.     int searchBarSpaceWidthPx;"white-space:pre">      // 搜索条  
  41.     int searchBarSpaceMaxWidthPx;  
  42.     int searchBarSpaceHeightPx;  
  43.     int searchBarHeightPx;  
  44.     int pageIndicatorHeightPx;  
  45.   
  46.     private static String APP_COUNTX = SystemProperties.get("ro.appscellcountx.value");  
  47.     private static String APP_COUNTY = SystemProperties.get("ro.appscellcounty.value");  
  48.     private static int MAX_ROW = 12;"white-space:pre">    // 最大行  
  49.     private static int MAX_COLS = 12;"white-space:pre">   // 最大列  
  50.     private static int MIN_ROW = 4;"white-space:pre"// 最小行  
  51.     private static int MIN_COLS = 4;"white-space:pre">    // 最小列  
  52.     private static boolean IS_CONFIG = !TextUtils.isEmpty(APP_COUNTX) & !TextUtils.isEmpty(APP_COUNTY);  
  53.     private static boolean IS_AVAILABLY = (Integer.parseInt(APP_COUNTX) > MIN_ROW)  &   
  54.                     (Integer.parseInt(APP_COUNTX) < MAX_ROW)  &  
  55.                     (Integer.parseInt(APP_COUNTY) > MIN_COLS) &   
  56.                                     (Integer.parseInt(APP_COUNTY) < MAX_COLS);  
  57.   
  58.     DeviceProfile(String n, float w, float h, float r, float c,  
  59.                   float is, float its, float hs, float his) {  
  60.         // Ensure that we have an odd number of hotseat items (since we need to place all apps)  
  61.         if (!AppsCustomizePagedView.DISABLE_ALL_APPS && hs % 2 == 0) {"white-space:pre">  // 热键数目必须是奇数,因为中间是应用抽屉的入口  
  62.             throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");  
  63.         }  
  64.   
  65.         name = n;  
  66.         minWidthDps = w;  
  67.         minHeightDps = h;  
  68.         numRows = r;  
  69.         numColumns = c;  
  70.         iconSize = is;  
  71.         iconTextSize = its;  
  72.         numHotseatIcons = hs;  
  73.         hotseatIconSize = his;  
  74.     }  
  75.   
  76.     DeviceProfile(Context context,  
  77.                   ArrayList profiles,  
  78.                   float minWidth, float minHeight,  
  79.                   int wPx, int hPx,  
  80.                   int awPx, int ahPx,  
  81.                   Resources resources) {  
  82.         DisplayMetrics dm = resources.getDisplayMetrics();  
  83.         ArrayList points =  
  84.                 new ArrayList();  
  85.         transposeLayoutWithOrientation =  
  86.                 resources.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);  
  87.         minWidthDps = minWidth;  
  88.         minHeightDps = minHeight;  
  89.   
  90.         ComponentName cn = new ComponentName(context.getPackageName(),  
  91.                 this.getClass().getName());  
  92.         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);  
  93.         edgeMarginPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);  
  94.         desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;  
  95.         pageIndicatorHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);  
  96.   
  97.         // 列  
  98.         for (DeviceProfile p : profiles) {  
  99.             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows));  
  100.         }  
  101.         numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));  
  102.         // 行  
  103.         points.clear();  
  104.         for (DeviceProfile p : profiles) {  
  105.             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));  
  106.         }  
  107.         numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));  
  108.         // 图标大小  
  109.         points.clear();  
  110.         for (DeviceProfile p : profiles) {  
  111.             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));  
  112.         }  
  113.         iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);  
  114.         iconSizePx = DynamicGrid.pxFromDp(iconSize, dm);  
  115.   
  116.         // 文字大小  
  117.         points.clear();  
  118.         for (DeviceProfile p : profiles) {  
  119.             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));  
  120.         }  
  121.         iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);  
  122.         iconTextSizePx = DynamicGrid.pxFromSp(iconTextSize, dm);  
  123.   
  124.         // 热键大小  
  125.         points.clear();  
  126.         for (DeviceProfile p : profiles) {  
  127.             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));  
  128.         }  
  129.         numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));  
  130.         // Interpolate the hotseat icon size  
  131.         points.clear();  
  132.         for (DeviceProfile p : profiles) {  
  133.             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize));  
  134.         }  
  135.         // 热键  
  136.         hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);  
  137.         hotseatIconSizePx = DynamicGrid.pxFromDp(hotseatIconSize, dm);  
  138.         hotseatAllAppsRank = (int) (numColumns / 2);  
  139.   
  140.         // Calculate other vars based on Configuration  
  141.         updateFromConfiguration(resources, wPx, hPx, awPx, ahPx);  
  142.   
  143.         // 搜索条  
  144.         searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);  
  145.         searchBarHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);  
  146.         searchBarSpaceWidthPx = Math.min(searchBarSpaceMaxWidthPx, widthPx);  
  147.         searchBarSpaceHeightPx = searchBarHeightPx + 2 * edgeMarginPx;  
  148.   
  149.         // 计算实际标题大小  
  150.         Paint textPaint = new Paint();  
  151.         textPaint.setTextSize(iconTextSizePx);  
  152.         FontMetrics fm = textPaint.getFontMetrics();  
  153.         cellWidthPx = iconSizePx;  
  154.         cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);  
  155.   
  156.         // At this point, if the cells do not fit into the available height, then we need  
  157.         // to shrink the icon size  
  158.         /* 
  159.         Rect padding = getWorkspacePadding(isLandscape ? 
  160.                 CellLayout.LANDSCAPE : CellLayout.PORTRAIT); 
  161.         int h = (int) (numRows * cellHeightPx) + padding.top + padding.bottom; 
  162.         if (h > availableHeightPx) { 
  163.             float delta = h - availableHeightPx; 
  164.             int deltaPx = (int) Math.ceil(delta / numRows); 
  165.             iconSizePx -= deltaPx; 
  166.             iconSize = DynamicGrid.dpiFromPx(iconSizePx, dm); 
  167.             cellWidthPx = iconSizePx; 
  168.             cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top); 
  169.         } 
  170.         */  
  171.   
  172.         // 热键  
  173.         hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;  
  174.         hotseatCellWidthPx = iconSizePx;  
  175.         hotseatCellHeightPx = iconSizePx;  
  176.   
  177.         // 应用文件夹  
  178.         folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;  
  179.         folderCellHeightPx = cellHeightPx + (int) ((3f/2f) * edgeMarginPx);  
  180.         folderBackgroundOffset = -edgeMarginPx;  
  181.         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;  
  182.     }  
  183.   
  184.     void updateFromConfiguration(Resources resources, int wPx, int hPx,  
  185.                                  int awPx, int ahPx) {  
  186.         isLandscape = (resources.getConfiguration().orientation ==  
  187.                 Configuration.ORIENTATION_LANDSCAPE);  
  188.         isTablet = resources.getBoolean(R.bool.is_tablet);  
  189.         isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);  
  190.         widthPx = wPx;  
  191.         heightPx = hPx;  
  192.         availableWidthPx = awPx;  
  193.         availableHeightPx = ahPx;  
  194.   
  195.         Rect padding = getWorkspacePadding(isLandscape ?  
  196.                 CellLayout.LANDSCAPE : CellLayout.PORTRAIT);  
  197.         int pageIndicatorOffset =  
  198.             resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);  
  199.       
  200.     if (IS_CONFIG && IS_AVAILABLY) {   
  201.         if (isLandscape) {   
  202.             allAppsNumRows = Integer.parseInt(APP_COUNTX);  
  203.             allAppsNumCols = Integer.parseInt(APP_COUNTY);  
  204.         } else {  
  205.             allAppsNumRows = Integer.parseInt(APP_COUNTY);  
  206.             allAppsNumCols = Integer.parseInt(APP_COUNTX);  
  207.         }  
  208.     } else {  
  209.             if (isLandscape) {  
  210.                     allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /  
  211.                         (iconSizePx + iconTextSizePx + 2 * edgeMarginPx);  
  212.             } else {  
  213.                     allAppsNumRows = (int) numRows + 1;  
  214.             }  
  215.             allAppsNumCols = (availableWidthPx - padding.left - padding.right - 2 * edgeMarginPx) /  
  216.                     (iconSizePx + 2 * edgeMarginPx);  
  217.     }  
  218.     }  
  219.   
  220.     private float dist(PointF p0, PointF p1) {  
  221.         return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +  
  222.                 (p1.y-p0.y)*(p1.y-p0.y));  
  223.     }  
  224.   
  225.     private float weight(PointF a, PointF b,  
  226.                         float pow) {  
  227.         float d = dist(a, b);  
  228.         if (d == 0f) {  
  229.             return Float.POSITIVE_INFINITY;  
  230.         }  
  231.         return (float) (1f / Math.pow(d, pow));  
  232.     }  
  233.   
  234.     private float invDistWeightedInterpolate(float width, float height,  
  235.                 ArrayList points) {  
  236.         float sum = 0;  
  237.         float weights = 0;  
  238.         float pow = 5;  
  239.         float kNearestNeighbors = 3;  
  240.         final PointF xy = new PointF(width, height);  
  241.   
  242.         ArrayList pointsByNearness = points;  
  243.         Collections.sort(pointsByNearness, new Comparator() {  
  244.             public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {  
  245.                 return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));  
  246.             }  
  247.         });  
  248.   
  249.         for (int i = 0; i < pointsByNearness.size(); ++i) {  
  250.             DeviceProfileQuery p = pointsByNearness.get(i);  
  251.             if (i < kNearestNeighbors) {  
  252.                 float w = weight(xy, p.dimens, pow);  
  253.                 if (w == Float.POSITIVE_INFINITY) {  
  254.                     return p.value;  
  255.                 }  
  256.                 weights += w;  
  257.             }  
  258.         }  
  259.   
  260.         for (int i = 0; i < pointsByNearness.size(); ++i) {  
  261.             DeviceProfileQuery p = pointsByNearness.get(i);  
  262.             if (i < kNearestNeighbors) {  
  263.                 float w = weight(xy, p.dimens, pow);  
  264.                 sum += w * p.value / weights;  
  265.             }  
  266.         }  
  267.   
  268.         return sum;  
  269.     }  
  270.   
  271.     Rect getWorkspacePadding(int orientation) {  
  272.         Rect padding = new Rect();  
  273.         if (orientation == CellLayout.LANDSCAPE &&  
  274.                 transposeLayoutWithOrientation) {  
  275.             // Pad the left and right of the workspace with search/hotseat bar sizes  
  276.             padding.set(searchBarSpaceHeightPx, edgeMarginPx,  
  277.                     hotseatBarHeightPx, edgeMarginPx);  
  278.         } else {  
  279.             if (isTablet()) {  
  280.                 // Pad the left and right of the workspace to ensure consistent spacing  
  281.                 // between all icons  
  282.                 int width = (orientation == CellLayout.LANDSCAPE)  
  283.                         ? Math.max(widthPx, heightPx)  
  284.                         : Math.min(widthPx, heightPx);  
  285.                 // XXX: If the icon size changes across orientations, we will have to take  
  286.                 //      that into account here too.  
  287.                 int gap = (int) ((width - 2 * edgeMarginPx -  
  288.                         (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));  

你可能感兴趣的:(Android,Android源码分析)