最近有个launcher项目,有个修改最近任务的功能。
改成普通安卓手机,按任务栏显示最近打开的任务界面。
可以上下滑动层叠卡片式布局,
卡片式布局可以参考我另一篇文章:Android7最近任务栏UI(DeckView)
一.获取最近任务
taskListLoader.loadTaskList(tasks -> {
try {
Collections.reverse(tasks);
for (Task task : tasks) {
AppBeanInfo appIconInfo = new AppBeanInfo();
task.titleDescription = task.titleDescription;
ComponentName component = task.getTopComponent();
String pkgName = component.getPackageName();
if (pkgName.equals("com.lzui.launcher")) continue;
Log.i("RecentsActivity 1", pkgName + "-" + task.getTopComponent());
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(pkgName, 0);
String applicationName = (String) pm.getApplicationLabel(applicationInfo);
Drawable icon = pm.getApplicationIcon(applicationInfo);
appIconInfo.setAppName(applicationName);
appIconInfo.setDrawable(icon);
appIconInfo.setPackageName(pkgName);
appIconInfo.setTask(task);
appIconInfoList.add(appIconInfo);
Log.i("RecentsActivity 1", applicationName);
} catch (Exception e) {
e.printStackTrace();
}
}
mDeckView.notifyDataSetChanged();
if(appIconInfoList.size() != 0){
mDeckView.scrollToChild(appIconInfoList.size() - 2);
}
} catch (Exception e) {
e.printStackTrace();
}
});
public void loadTaskList(@Nullable Consumer
if (!needsToLoad()) {
if (onLoadedCallback != null) {
onLoadedCallback.accept(mTaskList);
}
return;
}
// TODO: Look into error checking / more robust handling for when things go wrong.
mTaskListChangeId = mRecentsModel.getTasks(loadedTasks -> {
ArrayList
// Reverse tasks to put most recent at the bottom of the view
Collections.reverse(tasks);
// Load task content
for (Task task : tasks) {
int loadedPos = mTaskList.indexOf(task);
if (loadedPos == -1) {
continue;
}
Task loadedTask = mTaskList.get(loadedPos);
task.icon = loadedTask.icon;
task.titleDescription = loadedTask.titleDescription;
task.thumbnail = loadedTask.thumbnail;
}
mTaskList = tasks;
onLoadedCallback.accept(tasks);
});
}
TaskListLoader在源码
public final class TaskListLoader {
private final RecentsModel mRecentsModel;
private ArrayList
private int mTaskListChangeId;
public TaskListLoader(Context context) {
mRecentsModel = RecentsModel.INSTANCE.get(context);
}
/**
* Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
* read-only list. This list of tasks is not guaranteed to have all content loaded.
*
* @return the current list of tasks
*/
public List
return Collections.unmodifiableList(mTaskList);
}
/**
* Whether or not the loader needs to load data to be up to date. This can return true if the
* task list is already up to date OR there is already a load in progress for the task list to
* become up to date.
*
* @return true if already up to date or load in progress, false otherwise
*/
public boolean needsToLoad() {
return !mRecentsModel.isTaskListValid(mTaskListChangeId);
}
/**
* Fetches the most recent tasks and updates the task list asynchronously. This call does not
* provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
* what it has. May run the callback immediately if there have been no changes in the task
* list since the start of the last load.
*
* @param onLoadedCallback callback to run when task list is loaded
*/
public void loadTaskList(@Nullable Consumer
if (!needsToLoad()) {
if (onLoadedCallback != null) {
onLoadedCallback.accept(mTaskList);
}
return;
}
// TODO: Look into error checking / more robust handling for when things go wrong.
mTaskListChangeId = mRecentsModel.getTasks(loadedTasks -> {
ArrayList
// Reverse tasks to put most recent at the bottom of the view
Collections.reverse(tasks);
// Load task content
for (Task task : tasks) {
int loadedPos = mTaskList.indexOf(task);
if (loadedPos == -1) {
continue;
}
Task loadedTask = mTaskList.get(loadedPos);
task.icon = loadedTask.icon;
task.titleDescription = loadedTask.titleDescription;
task.thumbnail = loadedTask.thumbnail;
}
mTaskList = tasks;
onLoadedCallback.accept(tasks);
});
}
/**
* Load task icon and label asynchronously if it is not already loaded in the task. If the task
* already has an icon, this calls the callback immediately.
*
* @param task task to update with icon + label
* @param onLoadedCallback callback to run when task has icon and label
*/
public void loadTaskIconAndLabel(Task task, @Nullable Runnable onLoadedCallback) {
mRecentsModel.getIconCache().updateIconInBackground(task,
loadedTask -> onLoadedCallback.run());
}
/**
* Load thumbnail asynchronously if not already loaded in the task. If the task already has a
* thumbnail or if the thumbnail is cached, this calls the callback immediately.
*
* @param task task to update with the thumbnail
* @param onLoadedCallback callback to run when task has thumbnail
*/
public void loadTaskThumbnail(Task task, @Nullable Runnable onLoadedCallback) {
mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
thumbnail -> onLoadedCallback.run());
}
/**
* Removes the task from the current task list.
*/
public void removeTask(Task task) {
mTaskList.remove(task);
}
/**
* Clears the current task list.
*/
public void clearAllTasks() {
mTaskList.clear();
}
}
loadTaskList:加载最近任务列表
loadTaskThumbnail:加载最近任务缩略图
removeTask:移除最近任务列表
clearAllTasks:清楚全部最近任务
二、获取最近任务缩略图
private void loadViewDataInternal(final WeakReference
if (Build.VERSION.SDK_INT >= 26) {
taskListLoader.loadTaskThumbnail(appBeanInfo.getTask(), () -> {
weakView.get().onDataLoaded(appBeanInfo, appBeanInfo.getTask().thumbnail.thumbnail,
appBeanInfo.getDrawable(), appBeanInfo.getAppName(), Color.DKGRAY);
});
}
}
DeckView.Callback
@Override
public ArrayList
return appIconInfoList;
}
@Override
public void loadViewData(WeakReference
llNullContent.setVisibility(View.GONE);
llCleanAll.setVisibility(View.VISIBLE);
loadViewDataInternal(dcv, item);
}
@Override
public void unloadViewData(AppBeanInfo item) {
}
@Override
public void onViewDismissed(AppBeanInfo item) {
ActivityManagerWrapper.getInstance().removeTask(item.getTask().key.id);
taskListLoader.removeTask(item.getTask());
appIconInfoList.remove(item);
mDeckView.notifyDataSetChanged();
}
@Override
public void onItemClick(AppBeanInfo item) {
// 进入此程序
try {
Intent intent = new Intent();
//通过包名启动
PackageManager packageManager = getPackageManager();
intent = packageManager.getLaunchIntentForPackage(item.getPackageName());
if (null != intent) {
startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onNoViewsToDeck() {
llNullContent.setVisibility(View.VISIBLE);
llCleanAll.setVisibility(View.GONE);
}
@Override
public void onFinsh() {
RecentsActivity.this.finish();
}
};
三、清除全部任务
ActivityManagerWrapper.getInstance().removeAllRecentTasks();
taskListLoader.clearAllTasks();
appIconInfoList.clear();
appIconInfoList = null;
mDeckView = null;
单个清除
ActivityManagerWrapper.getInstance().removeTask(item.getTask().key.id);
taskListLoader.removeTask(item.getTask());
appIconInfoList.remove(item);
四、拿出10.0的源码修改成launcher用的。
代码有点多,我整理成资源,供大家下载,又不懂的欢迎留言。
主要代码类:
RecentsModel
public class RecentsModel extends TaskStackChangeListener {
private static final String TAG = "RecentsModel";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject
new MainThreadInitializedObject<>(c -> new RecentsModel(c));
private final List
private final Context mContext;
private ISystemUiProxy mSystemUiProxy;
private final RecentTasksList mTaskList;
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
private RecentsModel(Context context) {
mContext = context;
HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
Process.THREAD_PRIORITY_BACKGROUND);
loaderThread.start();
mTaskList = new RecentTasksList(context);
mIconCache = new TaskIconCache(context, loaderThread.getLooper());
mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper());
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
public TaskIconCache getIconCache() {
return mIconCache;
}
public TaskThumbnailCache getThumbnailCache() {
return mThumbnailCache;
}
/**
* Fetches the list of recent tasks.
*
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
* @return the request id associated with this call.
*/
public int getTasks(Consumer
return mTaskList.getTasks(false /* loadKeysOnly */, callback);
}
/**
* @return The task id of the running task, or -1 if there is no current running task.
*/
public static int getRunningTaskId() {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
return runningTask != null ? runningTask.id : -1;
}
/**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
public boolean isTaskListValid(int changeId) {
return mTaskList.isTaskListValid(changeId);
}
/**
* Finds and returns the task key associated with the given task id.
*
* @param callback The callback to receive the task key if it is found or null. This is always
* called on the UI thread.
*/
public void findTaskWithId(int taskId, Consumer
mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
for (Task task : tasks) {
if (task.key.id == taskId) {
callback.accept(task.key);
return;
}
}
callback.accept(null);
});
}
@Override
public void onTaskStackChangedBackground() {
if (!mThumbnailCache.isPreloadingEnabled()) {
// Skip if we aren't preloading
return;
}
int currentUserId = Process.myUserHandle().getIdentifier();
if (!TaskUtils.checkCurrentOrManagedUserId(currentUserId, mContext)) {
// Skip if we are not the current user
return;
}
// Keep the cache up to date with the latest thumbnails
int runningTaskId = RecentsModel.getRunningTaskId();
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) {
if (task.key.id == runningTaskId) {
// Skip the running task, it's not going to have an up-to-date snapshot by the
// time the user next enters overview
continue;
}
mThumbnailCache.updateThumbnailInCache(task);
}
});
}
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
if (task != null) {
task.thumbnail = snapshot;
}
}
}
@Override
public void onTaskRemoved(int taskId) {
Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
mThumbnailCache.remove(dummyKey);
}
public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
mSystemUiProxy = systemUiProxy;
}
public ISystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
}
public void onTrimMemory(int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
mThumbnailCache.getHighResLoadingState().setVisible(false);
}
if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
// Clear everything once we reach a low-mem situation
mThumbnailCache.clear();
mIconCache.clear();
}
}
public void onOverviewShown(boolean fromHome, String tag) {
if (mSystemUiProxy == null) {
return;
}
try {
mSystemUiProxy.onOverviewShown(fromHome);
} catch (RemoteException e) {
Log.w(tag,
"Failed to notify SysUI of overview shown from " + (fromHome ? "home" : "app")
+ ": ", e);
}
}
public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
public interface TaskThumbnailChangeListener {
Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
}
public RecentTasksList getmTaskList() {
return mTaskList;
}
}
TaskThumbnailCache
public class TaskThumbnailCache {
private final Handler mBackgroundHandler;
private final MainThreadExecutor mMainThreadExecutor;
private final int mCacheSize;
private final ThumbnailCache mCache;
private final HighResLoadingState mHighResLoadingState;
public static class HighResLoadingState {
private boolean mIsLowRamDevice;
private boolean mVisible;
private boolean mFlingingFast;
private boolean mHighResLoadingEnabled;
private ArrayList
public interface HighResLoadingStateChangedCallback {
void onHighResLoadingStateChanged(boolean enabled);
}
private HighResLoadingState(Context context) {
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIsLowRamDevice = activityManager.isLowRamDevice();
}
public void addCallback(HighResLoadingStateChangedCallback callback) {
mCallbacks.add(callback);
}
public void removeCallback(HighResLoadingStateChangedCallback callback) {
mCallbacks.remove(callback);
}
public void setVisible(boolean visible) {
mVisible = visible;
updateState();
}
public void setFlingingFast(boolean flingingFast) {
mFlingingFast = flingingFast;
updateState();
}
public boolean isEnabled() {
return mHighResLoadingEnabled;
}
private void updateState() {
boolean prevState = mHighResLoadingEnabled;
mHighResLoadingEnabled = !mIsLowRamDevice && mVisible && !mFlingingFast;
if (prevState != mHighResLoadingEnabled) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
mCallbacks.get(i).onHighResLoadingStateChanged(mHighResLoadingEnabled);
}
}
}
}
public TaskThumbnailCache(Context context, Looper backgroundLooper) {
mBackgroundHandler = new Handler(backgroundLooper);
mMainThreadExecutor = new MainThreadExecutor();
mHighResLoadingState = new HighResLoadingState(context);
Resources res = context.getResources();
mCacheSize = 3;
mCache = new ThumbnailCache(mCacheSize);
}
/**
* Synchronously fetches the thumbnail for the given {@param task} and puts it in the cache.
*/
public void updateThumbnailInCache(Task task) {
Preconditions.assertUIThread();
// Fetch the thumbnail for this task and put it in the cache
if (task.thumbnail == null) {
updateThumbnailInBackground(task.key, true /* reducedResolution */,
t -> task.thumbnail = t);
}
}
/**
* Synchronously updates the thumbnail in the cache if it is already there.
*/
public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
Preconditions.assertUIThread();
mCache.updateIfAlreadyInCache(taskId, thumbnail);
}
/**
* Asynchronously fetches the icon and other task data for the given {@param task}.
*
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
public ThumbnailLoadRequest updateThumbnailInBackground(
Task task, Consumer
Preconditions.assertUIThread();
boolean reducedResolution = !mHighResLoadingState.isEnabled();
if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
// Nothing to load, the thumbnail is already high-resolution or matches what the
// request, so just callback
callback.accept(task.thumbnail);
return null;
}
return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
task.thumbnail = t;
callback.accept(t);
});
}
private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
Consumer
Preconditions.assertUIThread();
ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
// Already cached, lets use that thumbnail
callback.accept(cachedThumbnail);
return null;
}
ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
reducedResolution) {
@Override
public void run() {
ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
key.id, reducedResolution);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
mMainThreadExecutor.execute(() -> {
mCache.put(key, thumbnail);
callback.accept(thumbnail);
onEnd();
});
}
};
Utilities.postAsyncCallback(mBackgroundHandler, request);
return request;
}
/**
* Clears the cache.
*/
public void clear() {
mCache.evictAll();
}
/**
* Removes the cached thumbnail for the given task.
*/
public void remove(TaskKey key) {
mCache.remove(key);
}
/**
* @return The cache size.
*/
public int getCacheSize() {
return mCacheSize;
}
/**
* @return The mutable high-res loading state.
*/
public HighResLoadingState getHighResLoadingState() {
return mHighResLoadingState;
}
/**
* @return Whether to enable background preloading of task thumbnails.
*/
public boolean isPreloadingEnabled() {
return !mHighResLoadingState.mIsLowRamDevice && mHighResLoadingState.mVisible;
}
public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
public final boolean reducedResolution;
ThumbnailLoadRequest(Handler handler, boolean reducedResolution) {
super(handler, null);
this.reducedResolution = reducedResolution;
}
}
private static class ThumbnailCache extends TaskKeyLruCache
public ThumbnailCache(int cacheSize) {
super(cacheSize);
}
/**
* Updates the cache entry if it is already present in the cache
*/
public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
ThumbnailData oldData = getCacheEntry(taskId);
if (oldData != null) {
putCacheEntry(taskId, thumbnailData);
}
}
}
}
还需要一个10.0的jar包,一起在资源里面了。
SystemUISharedLib.jar
10.0获取最近任务缩略图链接:10.0任务列表获取缩略图-Android文档类资源-CSDN下载