5.1 Launcher3 修改总结

最近比较忙 有段时间没写博客了   趁着有空 把最近修改launcher的心得总结一下


一、 修改和替换特定应用图标

   1   allApps界面

    allApps界面就是所谓的二级菜单,抽屉。

   Launcher3 生成二级菜单的图标分为初次加载和初次加载之后。

   初次加载时 修改 IconCache.java 中的 updateCacheAndGetContentValues()方法

public synchronized void getTitleAndIcon(AppInfo application,
            LauncherActivityInfoCompat info, boolean useLowResIcon) {
        UserHandleCompat user = info == null ? application.user : info.getUser();
        CacheEntry entry = cacheLocked(application.componentName, info, user,
                false, useLowResIcon);
        application.title = Utilities.trim(entry.title);
//        Bitmap icon = null;
//        icon = getNonNullIcon(entry, user);

        application.iconBitmap =  replaceFirstShownIcon(application,entry,user);
        application.contentDescription = entry.contentDescription;
        application.usingLowResIcon = entry.isLowResIcon;
    }
其中  AllApps页面初次加载时替换系统图标的方法如下,就是读取包名,然后替换
private Bitmap replaceFirstShownIcon(AppInfo info,CacheEntry entry,UserHandleCompat user){

        String pkgName = info.componentName.getPackageName();
        String className = info.componentName.getClassName();
        Resources resources = mContext.getResources();
        if(pkgName.equals("com.android.soundrecorder")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_record);
        }
        if(pkgName.equals("com.android.deskclock")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_clock);
        }
        if(pkgName.equals("com.android.qworldclock")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_world);
        }
        if(pkgName.equals("com.android.gallery3d")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_gallery);
        }
        if(pkgName.equals("com.android.music")){
            if(className.equals("com.android.music.VideoBrowserActivity")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_play);
            }
            if(className.equals("com.android.music.MusicBrowserActivity")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_music);
            }

        }

            if(pkgName.equals("com.android.providers.downloads.ui")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_download);
            }
            if(pkgName.equals("com.android.email")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_email);
            }
        if(pkgName.equals("com.android.mms")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_sms);
        }
            return getNonNullIcon(entry, user);
        }

  从IconCache.java的类名就可以看出 这个类是用作缓存系统已安装软件的图标的,在第一次加载成功后会将Icons缓存在一个app_icons.db的数据库

文件中,下次再读取就是从数据库中读取

   所以如果要修改AllApps界面图标,除了在replaceFirstShownIcon() 方法中做替换外,还要在写入数据库的方法中同样替换一次

该方法为IconCahce类的addIconToDb方法

private void addIconToDB(ContentValues values, ComponentName key,
            PackageInfo info, long userSerial) {
        values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
        values.put(IconDB.COLUMN_USER, userSerial);
        values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
        values.put(IconDB.COLUMN_VERSION, info.versionCode);
        replaceIcon(key,values);
        mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
                SQLiteDatabase.CONFLICT_REPLACE);
    }
同样,替换方法为

private void replaceIcon(ComponentName name,ContentValues values){
        if(name.getPackageName().equals("com.android.soundrecorder")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_record)));
        }
        if(name.getPackageName().equals("com.android.qworldclock")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_world)));
        }
        if(name.getPackageName().equals("com.android.gallery3d")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_gallery)));
        }
        if(name.getPackageName().equals("com.android.music")){
            if(name.getClassName().equals("com.android.music.VideoBrowserActivity")){
                values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_play)));
            }
            if(name.getClassName().equals("com.android.music.MusicBrowserActivity")){
                values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_music)));

            }
        }
        if(name.getPackageName().equals("com.android.providers.downloads.ui")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_download)));
        }
		if(name.getPackageName().equals("com.android.deskclock")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_clock)));
        }
        if(name.getPackageName().equals("com.android.email")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_email)));
        }
        if(name.getPackageName().equals("com.android.mms")){
            values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(BitmapFactory.decodeResource(mContext.getResources(),R.drawable.icon_sms)));
        }

    }

  2   workspace界面

为了达到替换图标的目的,在workspace同样需要做一些操作。

workspace生成图标的途径有很多,从allApps界面拖拽,从workspace中拖拽,或者从default_workspace.xml中读取,每一个途径,最终要在workspace界面生成图标,都必须会走Launcher.java中的creatShortCut()方法

public View createShortcut(ViewGroup parent, ShortcutInfo info) {
        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon,
                parent, false);
        favorite.applyFromShortcutInfo(info, mIconCache);
        favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
        favorite.setOnClickListener(this);
        favorite.setOnFocusChangeListener(mFocusHandler);
        return favorite;
    }

跟进favorite.applyFromShortcutInfo(info, mIconCache)中,

该方法在BubbleTextView中,这个BubbleTextView是一个TextView,代表桌面上的每一个icon,一个icon 其实就是一个textView(应用名称)和imageview的组合。

public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
            boolean promiseStateChanged) {
//        Bitmap b = info.getIcon(iconCache);
        Bitmap b= replaceIcon(info,iconCache);

        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
        iconDrawable.setGhostModeEnabled(info.isDisabled != 0);

        setIcon(iconDrawable, mIconSize);
        if (info.contentDescription != null) {
            setContentDescription(info.contentDescription);
        }
        setText(info.title);
        setTag(info);

        if (promiseStateChanged || info.isPromise()) {
            applyState(promiseStateChanged);
        }
    }
我们在这里面做替换系统图标的工作

 private Bitmap replaceIcon(ShortcutInfo info,IconCache cache){
        Resources resources = getContext().getResources();
        String pkgName = info.getTargetComponent().getPackageName();
        String className = info.getTargetComponent().getClassName();
        if(pkgName.equals("com.android.soundrecorder")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_record);
        }
        if(pkgName.equals("com.android.deskclock")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_clock);
        }
        if(pkgName.equals("com.android.qworldclock")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_world);
        }
        if(pkgName.equals("com.android.gallery3d")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_gallery);
        }
        if(pkgName.equals("com.android.music")){
            if(className.equals("com.android.music.VideoBrowserActivity")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_play);
            }
            if(className.equals("com.android.music.MusicBrowserActivity")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_music);
            }
        }

        if(pkgName.equals("com.android.providers.downloads.ui")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_download);
        }
        if(pkgName.equals("com.android.email")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_email);
        }
        if(pkgName.equals("com.android.mms")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_sms);
        }
        return info.getIcon(cache);

    }

3  更换系统语言之后


 当更换系统语言之后,图标和标签会重新加载生成一次,而且在AllApps列表里,图标的排列次序也会发生变化,跟进到IconCache.java的updateCacheAndGetContentValues()方法中,在这里做替换图标工作

@Thunk ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app,
            boolean replaceExisting) {
        final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser());
        CacheEntry entry = null;
        if (!replaceExisting) {
            entry = mCache.get(key);
            // We can't reuse the entry if the high-res icon is not present.
            if (entry == null || entry.isLowResIcon || entry.icon == null) {
                entry = null;
            }
        }
        if (entry == null) {
            entry = new CacheEntry();
            Bitmap icon = replaceIconAfterLanguageChanged(app);
            entry.icon = icon;
        }
        entry.title = app.getLabel();
        entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
        mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry);

        return newContentValues(entry.icon, entry.title.toString(), mActivityBgColor);
    }

其中 replaceIconAfterLanguageChanged()如下

private Bitmap replaceIconAfterLanguageChanged(LauncherActivityInfoCompat app){

    return IconReplaceUtil.replaceIcon(mContext,app,mIconDpi);

}

  public static  Bitmap replaceIcon(Context mContext,LauncherActivityInfoCompat app,int mIconDpi){
        String pkgName = app.getComponentName().getPackageName();
        String className = app.getComponentName().getClassName();
        Resources resources = mContext.getResources();
       Bitmap icon = replaceIcon(pkgName,className,resources);
        if(icon!=null){
            return icon;
        }
        else return Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext);
    }


public static Bitmap replaceIcon(String pkgName, String className,Resources resources){
        if(pkgName.equals("com.android.soundrecorder")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_record);
        }
        if(pkgName.equals("com.android.deskclock")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_clock);
        }
        if(pkgName.equals("com.android.qworldclock")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_world);
        }
        if(pkgName.equals("com.android.gallery3d")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_gallery);
        }
        if(pkgName.equals("com.android.music")){
            if(className.equals("com.android.music.VideoBrowserActivity")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_play);
            }
            if(className.equals("com.android.music.MusicBrowserActivity")){
                return BitmapFactory.decodeResource(resources,R.drawable.icon_music);
            }

        }

        if(pkgName.equals("com.android.providers.downloads.ui")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_download);
        }
        if(pkgName.equals("com.android.email")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_email);
        }
        if(pkgName.equals("com.android.calendar")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_calendar);
        }
        if(pkgName.equals("com.android.mms")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_sms);
        }
        if(pkgName.equals("com.android.calculator2")){
            return BitmapFactory.decodeResource(resources,R.drawable.icon_calculator2);
        }


到这里,基本上替换图标的工作就完成了  完成AllApps界面,拖拽到workspace,更改系统语言都不会失效。

二 、修改主页默认布局

 1 主页布局包括 行列数, 默认桌面图标和widget,hotseat 默认图标。

 行列数 修改InvariantDeviceProfile.java中 几个属性值就好

这里大概说明一下

public int numRows; // workspace 行
    public int numColumns; //workspace 列
public int numFolderRows; // 文件夹中图标行
    public int numFolderColumns; //文件件中图标的列
    float iconSize; 图标大小
    float iconTextSize;  图标标题大小 
float numHotseatIcons; 热区图表数
    float hotseatIconSize; 热区图表大小
    int defaultLayoutId;  默认加载的桌面布局属性xml文件的资源id

修改相应属性值即可生效 

比如如果要让系统强制加载默认的5*5布局,就在 defaultLayoutId赋值的两个方法中,将id写死为R.xml.default_workspace5*5即可。

其他配置方法同上。

id写死为该xml文件,就要在该xml中配置默认的一些属性,比如你想默认放置哪些图标和插件(4.4之后默认放置插件的权限被谷歌提高,普通应用设置该属性无效,必须打包进system/priv-app中才可以设置默认小插件)在桌面上,就配置工程中的res/xml/default_workspace_4*4 5*5 5*6 这几个xml文件。

配置方法如下所示



    
      //这是hotseat的默认配置 我放在下面


        
        
    
    
        

    
    
        

    
    
        

    

    
        
    

    


    




hotseat的配置和上面基本是一样的,注意有两个xml文件,代表手机和平板的。


    
    
   

    
        
        
        
        
        
    

    

   

    
    
    
    
    
    
    
    
   



三 过滤特定图标不在Allapps界面显示

 这个主要在LaunchModel.java的 loadAllApps方法中

 private void loadAllApps() {
            final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;

            final Callbacks oldCallbacks = mCallbacks.get();
                              ....
 
                // Create the ApplicationInfos
                for (int i = 0; i < apps.size(); i++) {
                    if("com.android.dialer".equals(apps.get(i).getApplicationInfo().packageName)
                            ||"com.caf.fmradio".equals(apps.get(i).getApplicationInfo().packageName)){
                          continue;    //过滤操作就在这里  所有app信息会加载后存入mBgAllAppsList 中

                    }
                  //  replaceIcon(apps.get(i));

                        LauncherActivityInfoCompat app = apps.get(i);

                        // This builds the icon bitmaps.
                        mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
                 }

                final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
                if (heuristic != null) {
                    final Runnable r = new Runnable() {

                        @Override
                        public void run() {
                            heuristic.processUserApps(apps);
                        }
                    };
                    runOnMainThread(new Runnable() {

                        @Override
                        public void run() {
                         ......
            }
        }


Launcher3的修改基本都只是一些小修改,整体难度不高



你可能感兴趣的:(java,Android)