时间: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();
}
}