launcher allapps界面添加favoriteApps常用的显示和分割线

时间:2020/12/29
之前公司不允许csdn,笔记写在其它地方。最近整理过来

需求描述

应用列表界面最上方添加常用应用和分割线

实现思路

修改allapps adapter数据,在allapps数据前五个进行添加常用应用,第六个添加分割线
优势:方便修改,不用添加view和调整坐标
缺陷:数据混搭在一起

最终效果图

在这里插入图片描述
后来发现原生已经支持此功能,只是要更新gms包。功能介绍看这里
这个自己的实现就做个记录吧。真是愚钝啊啊啊啊啊

代码实现

1、获取常用应用数据

(参考setting中的常用应用统计 暗码 ##4636## 进入Testing界面,选择usage statistics )

从setting摘出来获取常用应用和根据使用时间排序的逻辑,整理成一个工具类使用
UsageStatsUtils类,

package sprocomm.favorite;

import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.ArrayMap;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import android.util.Log;
import android.app.Activity;
import android.util.Log;
/**
*  *#*#4636#*#*
**/
public class UsageStatsUtils {
    private static final String TAG = "UsageStatsUtils";

    // Constants defining order for display order
    private final int _DISPLAY_ORDER_USAGE_TIME = 0;
    private final int _DISPLAY_ORDER_LAST_TIME_USED = 1;
    private final int _DISPLAY_ORDER_APP_NAME = 2;

    private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME;

    private  UsageStatsManager mUsageStatsManager;
    private PackageManager mPm;

    private final ArrayMap<String, String> mAppLabelMap = new ArrayMap<>();
    private final ArrayList<UsageStats> mPackageStats = new ArrayList<>();

    private AppNameComparator mAppLabelComparator;
    private UsageTimeComparator mUsageTimeComparator = new UsageTimeComparator();
    private LastTimeUsedComparator mLastTimeUsedComparator = new LastTimeUsedComparator();

    public ArrayList<UsageStats> getFavoriteApps(Activity launcher){
        mPm = launcher.getPackageManager();
        mUsageStatsManager = (UsageStatsManager) launcher.getSystemService(Context.USAGE_STATS_SERVICE);
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, -5);

        final List<UsageStats> stats =
                mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
                        cal.getTimeInMillis(), System.currentTimeMillis());
        if (stats == null) {
            return null;
        }

        ArrayMap<String, UsageStats> map = new ArrayMap<>();
        final int statCount = stats.size();
        for (int i = 0; i < statCount; i++) {
            final android.app.usage.UsageStats pkgStats = stats.get(i);


            // load application labels for each application
            try {
                ApplicationInfo appInfo = mPm.getApplicationInfo(pkgStats.getPackageName(), 0);
                String label = appInfo.loadLabel(mPm).toString();
                mAppLabelMap.put(pkgStats.getPackageName(), label);


                UsageStats existingStats =
                        map.get(pkgStats.getPackageName());
                if (existingStats == null) {
                    map.put(pkgStats.getPackageName(), pkgStats);
                } else {
                    existingStats.add(pkgStats);
                }


            } catch (PackageManager.NameNotFoundException e) {
                // This package may be gone.
            }
        }
        mPackageStats.addAll(map.values());


        // Sort list
        mAppLabelComparator = new AppNameComparator(mAppLabelMap);
        sortList();
        return mPackageStats;
    }
    private void sortList() {
        if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) {
            Log.i(TAG, "Sorting by usage time");
            Collections.sort(mPackageStats, mUsageTimeComparator);
        } else if (mDisplayOrder == _DISPLAY_ORDER_LAST_TIME_USED) {
            Log.i(TAG, "Sorting by last time used");
            Collections.sort(mPackageStats, mLastTimeUsedComparator);
        } else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) {
            Log.i(TAG, "Sorting by application name");
            Collections.sort(mPackageStats, mAppLabelComparator);
        }
    }

    public static class AppNameComparator implements Comparator<UsageStats> {
        private Map<String, String> mAppLabelList;


        AppNameComparator(Map<String, String> appList) {
            mAppLabelList = appList;
        }


        @Override
        public final int compare(UsageStats a, UsageStats b) {
            String alabel = mAppLabelList.get(a.getPackageName());
            String blabel = mAppLabelList.get(b.getPackageName());
            return alabel.compareTo(blabel);
        }
    }

    public static class LastTimeUsedComparator implements Comparator<UsageStats> {
        @Override
        public final int compare(UsageStats a, UsageStats b) {
            // return by descending order
            return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed());
        }
    }

    public static class UsageTimeComparator implements Comparator<UsageStats> {
        @Override
        public final int compare(UsageStats a, UsageStats b) {
            return Long.compare(b.getTotalTimeInForeground(), a.getTotalTimeInForeground());
        }
    }
}
数据更新

(1.allapps数据加载的时候插入、2.allapps界面onResume更新、3.进入allapps界面动画结束后更新)

packages/apps/Launcher3/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
中添加方法,调用工具类获取前几个包名,并从allapps列表中选出一行应用显示。

private boolean updateFavoriteApps(){
    boolean needUpdateFavoriteApps = false;
    //allAppsShowFavoriteApps功能开关  hasFilter搜索的时候不显示常用应用
    if(allAppsShowFavoriteApps && !hasFilter())
    {
        List<AppInfo> mTmpFavoriteApps = new ArrayList<>();
        UsageStatsUtils mUsageStatsUtils = new UsageStatsUtils();
        ArrayList<UsageStats> mPackageStats = mUsageStatsUtils.getFavoriteApps(mLauncher);
        Log.d(TAG,"mPackageStats: "+mPackageStats);
        for(UsageStats us : mPackageStats){//循环常用应用列表
            String favoriteAppPacnakeName = us.mPackageName;
            for (AppInfo info : mApps){//循环allapps列表
                String apppackageName = info.componentName.getPackageName();
                if(apppackageName.equals(favoriteAppPacnakeName)){//包名匹配跳出
                    mTmpFavoriteApps.add(info);
                    Log.d(TAG,"favoriteAppPacnakeName: "+favoriteAppPacnakeName);
                    break;
                }
            }
            if(mTmpFavoriteApps.size() >= mNumAppsPerRow){//满一行后跳出
                break;
            }
        }
        Log.d(TAG,"mTmpFavoriteApps: "+mTmpFavoriteApps);
        if(mTmpFavoriteApps.equals(mFavoriteApps)){//临时list和常用应用list对比,减少更新次数
            Log.d(TAG,"no need update");
            needUpdateFavoriteApps = false;
        }else{
            Log.d(TAG," need update");
            mFavoriteApps.clear();
            mFavoriteApps.addAll(mTmpFavoriteApps);
            needUpdateFavoriteApps = true;
        }
        Log.d(TAG," needUpdateFavoriteApps: "+needUpdateFavoriteApps);
        mTmpFavoriteApps.clear();
    }
    return needUpdateFavoriteApps;//返回是否需要更新
}
/**
**上面方法的变体,数据更新在线程中,防止卡主主菜单动画
**/
public synchronized void updateFavoriteAppsOnThread(){
    new Thread(new Runnable(){
        public void run(){
            boolean needUpdateUI = updateFavoriteApps();
            if(needUpdateUI){
                mLauncher.runOnUiThread(new Runnable(){
                    public void run(){
                        Log.d(TAG," mLauncher.runOnUiThread: ");
                        updateAdapterItems();
                    }
                });
            }
        }
    }).start();
}
更新时机1

allapps界面更新数据时,顺便更新常用应用

packages/apps/Launcher3/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
的onAppsUpdated中,
allapps刷新自己的数据,
step1 清除列表
step2 重新获取应用列表数据
step3 列表排序
step4 添加我们的常用应用数据获取
step5 updateAdapterItems 更新列表adapter
...
updateFavoriteApps();//step4
// Recompose the set of adapter items from the current set of apps
updateAdapterItems();//step5
private void updateAdapterItems() {
    Log.d(TAG," updateAdapterItems ");
    refillAdapterItems();
    refreshRecyclerView();
}
private void refillAdapterItems() {
    String lastSectionName = null;
    FastScrollSectionInfo lastFastScrollerSectionInfo = null;
    int position = 0;
    int appIndex = 0;


    // Prepare to update the list of sections, filtered apps, etc.
    mFilteredApps.clear();
    mFastScrollerSections.clear();
    mAdapterItems.clear();


    // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
    // ordered set of sections
    Log.d(TAG,"refillAdapterItems step1 add mFavoriteApps ");
    if(allAppsShowFavoriteApps && !hasFilter()){
        for(AppInfo info :mFavoriteApps){//添加常用应用
            String sectionName = getAndUpdateCachedSectionName(info.title);
            // Create a new section if the section names do not match
            if (!sectionName.equals(lastSectionName)) {
                lastSectionName = sectionName;
                lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
                mFastScrollerSections.add(lastFastScrollerSectionInfo);
            }
            // Create an app item
            AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
            if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
                lastFastScrollerSectionInfo.fastScrollToItem = appItem;
            }
            mAdapterItems.add(appItem);
            mFilteredApps.add(info);
        }
        if(allAppsShowFavoriteApps && !hasFilter() && mAdapterItems.size()== mNumAppsPerRow){//添加分割线
            mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
        }
    }
    Log.d(TAG,"refillAdapterItems step2 add mApps ");
    //添加主菜单应用,这里是原逻辑 没有动,
    //我们在主菜单列表数据前面添加了自己的数据和分割线
    for (AppInfo info : getFiltersAppInfos()) {
更新时机2

当用户在主菜单界面进入应用后,返回主菜单,这时候常用应用应该更新一次数据,

packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java

public void updateFavoriteApps(){
    if(mLauncher.isInState(ALL_APPS)){//这里为了减少调用次数,只有当前在主菜单并且OnResume才会走
        getApps().updateFavoriteAppsOnThread();
    }
}

在launcher的onResume中调用更新方法

getAppsView().updateFavoriteApps();
更新时机3

如果用户在home界面使用应用,onResume到home界面,这时候上面的逻辑不会更新常用应用数据,这是有问题的
我们需要在launcher滑入allapps界面之后,更新数据

packages/apps/Launcher3/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
我们给它添加launcher状态改变的监听回调
public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener, LauncherStateManager.StateListener
构造方法中添加注册监听
mLauncher.getStateManager().addStateListener(this);

@Override
public void onStateTransitionStart(LauncherState toState) {
}

@Override
public void onStateTransitionComplete(LauncherState finalState) {
    if(finalState == ALL_APPS){
    //这里因为是allapps动画过程结束的数据更新,需要在线程中进行,防止卡主动画
        updateFavoriteAppsOnThread();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值