1.简介
- 这里主要介绍下点击导航栏的recents按钮,跳转的页面。
- 我以前以为这东西是个单独的app,看了代码才发现,它是launcher的一部分。
- 我们打开源码/packages/apps/Launcher3 目录下,发现除了res,src,清单文件外,还有2个目录,go目录是给低配置的机器用的简化版的代码,quickstep是我们要看的东西。
1.1.Android.bp
打开Launcher3目录下的这个文件,搜索下android_app,可以发现有4个,也就是说这个工程可以编译4种apk
android_app {
name: "Launcher3",
android_app {
name: "Launcher3Go",
// Build rule for Quickstep app.
android_app {
name: "Launcher3QuickStep",
// Build rule for Launcher3 Go app with quickstep for Android Go devices.
android_app {
name: "Launcher3QuickStepGo",
- 那么image里用的哪种?这就要看配置了
- 官方源码默认的是build/target/product/handheld_system_ext.mk,我这编译的是phone用的,可以看到下边用的Launcher3QuickStep
# /system_ext packages
PRODUCT_PACKAGES += \
Launcher3QuickStep \
Provision \
Settings \
StorageManager \
SystemUI \
WallpaperCropper \
1.2.布局相关
底层布局还是上一篇里用到launcher.xml,里边有个这样的include,在默认的res下是空的,在quickstep里重写了
<include
android:id="@+id/overview_panel"
layout="@layout/overview_panel" />
</com.android.launcher3.dragndrop.DragLayer>
上图的1和2分别是下边的1和2布局
>1.overview_panel.xml
<merge >
<com.android.quickstep.views.LauncherRecentsView
android:id="@+id/overview_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:accessibilityPaneTitle="@string/accessibility_recent_apps"
android:clipChildren="false"
android:clipToPadding="false"
android:visibility="invisible" />
<include
android:id="@+id/overview_actions_view"
layout="@layout/overview_actions_container" />
</merge>
>2.overview_actions_container.xml
<com.android.quickstep.views.OverviewActionsView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom">
<LinearLayout
android:id="@+id/action_buttons"
android:layout_width="match_parent"
android:layout_height="@dimen/overview_actions_height"
android:layout_gravity="bottom|center_horizontal"
android:orientation="horizontal">
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Button
android:id="@+id/action_screenshot"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_screenshot"
android:text="@string/action_screenshot"
android:theme="@style/ThemeControlHighlightWorkspaceColor" />
<Space
android:id="@+id/action_split_space"
android:layout_width="@dimen/overview_actions_button_spacing"
android:layout_height="1dp"
android:visibility="gone" />
<Button
android:id="@+id/action_split"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action_split"
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<!--三键导航可见,右侧比重是2,手势导航不可见,右侧比重是1-->
<Space
android:id="@+id/oav_three_button_space"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:visibility="gone" />
</LinearLayout>
</com.android.quickstep.views.OverviewActionsView>
1.3.LauncherState
顾名思义,就是桌面的状态,有10种,这里就注释下我了解的几种
private static final LauncherState[] sAllStates = new LauncherState[10];
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
case NORMAL_STATE_ORDINAL:
return "Normal";//点击home键回到的页面就是,默认启动就是这个
case SPRING_LOADED_STATE_ORDINAL:
return "SpringLoaded";//拖动图标的时候,顶部功能按钮(removed)出现的时候
case OVERVIEW_STATE_ORDINAL:
return "Overview";//recents要显示的
case OVERVIEW_MODAL_TASK_STATE_ORDINAL:
return "OverviewModal";
case QUICK_SWITCH_STATE_ORDINAL:
return "QuickSwitch";
case ALL_APPS_STATE_ORDINAL:
return "AllApps";//桌面上划显示所有app图标的那个
case BACKGROUND_APP_STATE_ORDINAL:
//launcher在后台的时候,点击recents,先变成这个状态,再变成overview状态
return "Background";
case HINT_STATE_ORDINAL:
return "Hint";
case HINT_STATE_TWO_BUTTON_ORDINAL:
return "Hint2Button";
case OVERVIEW_SPLIT_SELECT_ORDINAL://分屏的时候,选择完第一个应用的时候
return "OverviewSplitSelect";
default:
return "Unknown";
}
}
1.4.StateManager.java
>1.addStateListener
LauncherRecentsView里用到这个,监听状态的改变好更改自己的可见性。
public void addStateListener(StateListener listener) {
mListeners.add(listener);
}
>2.goToState
private void goToState(
STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
animated &= areAnimatorsEnabled();
//要改变的状态和activity当前状态一样,直接回调即可
if (mActivity.isInState(state)) {
if (mConfig.currentAnimation == null) {
// Run any queued runnable
if (listener != null) {
listener.onAnimationEnd(null);
}
return;
} else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
// We are running the same animation as requested
if (listener != null) {
mConfig.currentAnimation.addListener(listener);
}
return;
}
}
//不需要动画
if (!animated) {
mAtomicAnimationFactory.cancelAllStateElementAnimation();
//回调listener
onStateTransitionStart(state);
//有handler的话调用下对应的方法
for (StateHandler handler : getStateHandlers()) {
handler.setState(state);
}
//回调listener
onStateTransitionEnd(state);
// Run any queued runnable
if (listener != null) {
listener.onAnimationEnd(null);
}
return;
}
if (delay > 0) {
//延迟后继续调用下边的方法
} else {
//带动画过程的,不看了
goToStateAnimated(state, fromState, listener);
}
}
2.LauncherRecentsView
点击recents按钮显示的页面就是这个容器
public class LauncherRecentsView extends RecentsView<QuickstepLauncher, LauncherState>
implements StateListener<LauncherState> {
2.1.构造方法
- 构造方法里添加监听的方法,获取state状态,同步修改自己的可见性。
public LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
//这里添加了state改变的监听,监听见补充1和2
mActivity.getStateManager().addStateListener(this);
}
>1.onStateTransitionStart
状态改变开始回调
public void onStateTransitionStart(LauncherState toState) {
//见3.9,更新overview可用状态
setOverviewStateEnabled(toState.overviewUi);
//overview页面显示的是否是网格格式,和LauncherState有关,overview状态下,平板设备返回true
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
}
setFreezeViewVisibility(true);
}
>2.onStateTransitionComplete
状态改变结束回调
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
reset();
}
//点击recent的话状态就是overview
boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
//见3.9
setOverlayEnabled(isOverlayEnabled);
setFreezeViewVisibility(false);
if (finalState != OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(false);
}
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
}
2.2.init
public void init(OverviewActionsView actionsView,
SplitSelectStateController splitPlaceholderView) {
super.init(actionsView, splitPlaceholderView);
setContentAlpha(0);//可以看到,默认是不可见的
}
2.3.startHome
public void startHome() {
//home页就是normal状态
mActivity.getStateManager().goToState(NORMAL);
//关闭所有其他额外添加的控件
AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
2.4.onTaskLaunchAnimationEnd
task加载动画结束以后调用
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
mActivity.getStateManager().moveToRestState();//状态恢复默认的
} else {
LauncherState state = mActivity.getStateManager().getState();
//获取当前状态,设置
mActivity.getAllAppsController().setState(state);
}
super.onTaskLaunchAnimationEnd(success);
}
2.5.initiateSplitSelect
初始化第一个分屏数据的时候,这种是在overview页面选择一个task作为第一个split的
public void initiateSplitSelect(TaskView taskView,
@SplitConfigurationOptions.StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent) {
super.initiateSplitSelect(taskView, stagePosition, splitEvent);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}
>1.initiateSplitSelect
这种是外部调用的,比如桌面长按弹框选择分屏,会传递数据过来
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
super.initiateSplitSelect(splitSelectSource);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}
2.6.canLaunchFullscreenTask
- 正常情况下,在overview页面,点击一个taskview就直接进入对应的应用了
- 在选择完第一个分屏的情况下,点击taskview是进入分屏操作的,点击事件开头会调用这个方法判断状态
protected boolean canLaunchFullscreenTask() {
return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
}
3.RecentsView
可以看到,又是继承的PagedView,添加child就可以左右滑动了。
public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener {
3.1.构造方法
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
BaseActivityInterface sizeStrategy) {
super(context, attrs, defStyleAttr);
setEnableFreeScroll(true);
mSizeStrategy = sizeStrategy;
mActivity = BaseActivity.fromContext(context);
//..数据的获取都在这model里
mModel = RecentsModel.INSTANCE.get(context);
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
//加载clearButton布局
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
mClearAllButton.setOnClickListener(this::dismissAllTasks);
//缓存一些我们要用到的view,构造方法里有默认的布局
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
mGroupedTaskViewPool = new ViewPool<>(context, this,
R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
5 /* max size */, 1 /* initial size */);
//初始化empty相关的图标和文字
mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
mEmptyIcon.setCallback(this);
mEmptyMessage = context.getText(R.string.recents_empty_message);
mEmptyMessagePaint = new TextPaint();
mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
mEmptyMessagePaint.setTextSize(getResources()
.getDimension(R.dimen.recents_empty_message_text_size));
mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
Typeface.NORMAL));
mEmptyMessagePaint.setAntiAlias(true);
mEmptyMessagePadding = getResources()
.getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
setWillNotDraw(false);
updateEmptyMessage();
//...
}
3.2.init
- 初始化action view,就是1.2.1布局的根容器,是个自定义的帧布局
- QuickstepLauncher.java的setupViews方法里会调用
public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
mActionsView = actionsView;
//taskview数为0隐藏,不为0显示
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
mSplitSelectStateController = splitController;
}
3.3.onAttachedToWindow
主要是添加一堆监听
protected void onAttachedToWindow() {
super.onAttachedToWindow();
updateTaskStackListenerState();//见3.9.1刷新数据
mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
.setSyncTransactionApplier(mSyncTransactionApplier));
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
mIPipAnimationListener);
mOrientationState.initListeners();
mTaskOverlayFactory.initListeners();
}
3.4.maybeDrawEmptyMessage
如果没有recent内容的时候,显示的空白提示,一个图片,一段文字,如下
public void draw(Canvas canvas) {
maybeDrawEmptyMessage(canvas);
super.draw(canvas);
}
protected void maybeDrawEmptyMessage(Canvas canvas) {
if (mShowEmptyMessage && mEmptyTextLayout != null) {
mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
canvas.save();
canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
(mTempRect.top - mTempRect.bottom) / 2);
//画图标
mEmptyIcon.draw(canvas);
canvas.translate(mEmptyMessagePadding,
mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
//画文字
mEmptyTextLayout.draw(canvas);
canvas.restore();
}
}
3.5.taskView可见性
- 手机模式最多可以显示3个taskView,中间一个完整显示的,以及两边部分显示的。
- 平板模式,除了一个完整的大个的,其他的都是2个一组显示的
>1.isTaskViewVisible
public boolean isTaskViewVisible(TaskView tv) {
if (showAsGrid()) {
//平板模式走这里,判断tv的范围
int screenStart = mOrientationHandler.getPrimaryScroll(this);
int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
return isTaskViewWithinBounds(tv, screenStart, screenEnd);
} else {
//手机模式走这里,只有中心那个,和它两边临近的两个可见,所以判断索引差值是1即可
return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
}
}
>2.isTaskViewFullyVisible
public boolean isTaskViewFullyVisible(TaskView tv) {
if (showAsGrid()) {
int screenStart = mOrientationHandler.getPrimaryScroll(this);
int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
return isTaskViewFullyWithinBounds(tv, screenStart, screenEnd);
} else {
// For now, just check if it's the active task
return indexOfChild(tv) == getNextPage();
}
}
>3.showAsGrid
- 是否是网格格式,这个是launcher的state有关
- 见2.1.1,有设置mOverviewGridEnabled这个值,简单来说就是平板模式为true
public boolean showAsGrid() {
return mOverviewGridEnabled || (mCurrentGestureEndTarget != null
&& mSizeStrategy.stateFromGestureEndTarget(
mCurrentGestureEndTarget).displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
}
3.6.resetTaskVisuals
重新设置下taskView的属性,调用地方很多
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
TaskView taskView = requireTaskViewAt(i);
if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
taskView.resetViewTransforms();
taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
taskView.setStableAlpha(mContentAlpha);
taskView.setFullscreenProgress(mFullscreenProgress);
taskView.setModalness(mTaskModalness);
taskView.setTaskThumbnailSplashAlpha(mTaskThumbnailSplashAlpha);
}
}
runActionOnRemoteHandles(remoteTargetHandle -> {
TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
simulator.taskPrimaryTranslation.value = 0;
simulator.taskSecondaryTranslation.value = 0;
simulator.fullScreenProgress.value = 0;
simulator.recentsViewScale.value = 1;
});
if (!mRunningTaskShowScreenshot) {
setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot);
}
if (mRunningTaskTileHidden) {
setRunningTaskHidden(mRunningTaskTileHidden);
}
updateCurveProperties();
// Update the set of visible task's data
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setTaskModalness(0);
setColorTint(0);
}
3.7.setVisibility
设置自己的可见性,并同步修改action view的flag
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (mActionsView != null) {
mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
if (visibility != VISIBLE) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
}
}
>1.setContentAlpha
-
设置透明度,为0的时候就不可见了。调用地方见3.8
-
初始化的时候透明度为0,也就是默认启动的时候是不可见的,见2.2的init方法
public void setContentAlpha(float alpha) { if (alpha == mContentAlpha) { return; } alpha = Utilities.boundToRange(alpha, 0, 1); mContentAlpha = alpha; int runningTaskId = getTaskIdsForRunningTaskView()[0]; for (int i = getTaskViewCount() - 1; i >= 0; i--) { TaskView child = requireTaskViewAt(i); int[] childTaskIds = child.getTaskIds(); if (!mRunningTaskTileHidden || (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) { child.setStableAlpha(alpha); } } mClearAllButton.setContentAlpha(mContentAlpha); int alphaInt = Math.round(alpha * 255); mEmptyMessagePaint.setAlpha(alphaInt); mEmptyIcon.setAlpha(alphaInt); mActionsView.getContentAlpha().setValue(mContentAlpha); if (alpha > 0) { setVisibility(VISIBLE); } else if (!mFreezeViewVisibility) { //alpha 为0的时候就不可见了。 setVisibility(INVISIBLE); } }
3.8.RecentsViewStateController
>1.和状态管理器绑定
StateManager.java
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
ArrayList<StateHandler> handlers = new ArrayList<>();
//见QuickstepLauncher
mActivity.collectStateHandlers(handlers);
mStateHandlers = handlers.toArray(new StateHandler[handlers.size()]);
}
return mStateHandlers;
}
QuickstepLauncher.java
protected void collectStateHandlers(List<StateHandler> out) {
super.collectStateHandlers(out);
out.add(getDepthController());
out.add(new RecentsViewStateController(this));//这里
}
>2.setState
- overview的可见性是通过改变state来实现的。
## BaseRecentsViewStateController.java
public void setState(@NonNull LauncherState state) {
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
//见补充3overview状态,透明度为1可见,非overview状态,透明度为0,不可见
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
RECENTS_GRID_PROGRESS.set(mRecentsView,
state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, state.showTaskThumbnailSplash() ? 1f : 0f);
}
>3.getContentAlphaProperty
FloatProperty<RecentsView> getContentAlphaProperty() {
return CONTENT_ALPHA;
}
public static final FloatProperty<RecentsView> CONTENT_ALPHA =
new FloatProperty<RecentsView>("contentAlpha") {
@Override
public void setValue(RecentsView view, float v) {
view.setContentAlpha(v);//见3.7.1
}
@Override
public Float get(RecentsView view) {
return view.getContentAlpha();
}
};
3.9.setOverviewStateEnabled
-
overview状态的话这个为true,非overview的时候又成了false,见2.1调用
public void setOverviewStateEnabled(boolean enabled) { mOverviewStateEnabled = enabled; //更新数据,补充1 updateTaskStackListenerState(); mOrientationState.setRotationWatcherEnabled(enabled); if (!enabled) { // mTmpRunningTasks = null; mSplitBoundsConfig = null; } updateLocusId(); }
>1.updateTaskStackListenerState
-
mHandleTaskStackChanges默认是false
-
进入overview状态handleTaskStackChanges基本就是true了,退出以后就是false
private void updateTaskStackListenerState() { boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow() && getWindowVisibility() == VISIBLE; if (handleTaskStackChanges != mHandleTaskStackChanges) { mHandleTaskStackChanges = handleTaskStackChanges; //为true的时候,也就是每次进入overview if (handleTaskStackChanges) { reloadIfNeeded();//加载数据 } } }
>2.reloadIfNeeded
public void reloadIfNeeded() {
//验证下旧数据是否有效,比如我们新打开一个app,这时候数据变化了,就无效了
if (!mModel.isTaskListValid(mTaskListChangeId)) {//见4.3
//重新获取数据,见4.4,数据回调处理见3.10
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
.getFilter(mFilterState.getPackageNameToFilter()));
}
}
3.10.applyLoadPlan
获取数据以后的回调了,主要处理child的添加
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
if (mPendingAnimation != null) {
mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
return;
}
mLoadPlanEverApplied = true;
//数据为null的情况
if (taskGroups == null || taskGroups.isEmpty()) {
//移除所有相关的view
removeTasksViewsAndClearAllButton();
//显示no recent的提示
onTaskStackUpdated();
//重置状态
resetTouchState();
if (isPageScrollsInitialized()) {
onPageScrollsInitialized();
}
return;
}
//..中间一堆代码就是获取容器旧的状态,方便后边恢复,因为下边会移除所有的child
removeAllViews();
//..
//需要移除的task,比如我们从桌面启动一个分屏,它已经在recent列表里了。
Task stagedTaskToBeRemovedFromGrid =
mSplitSelectSource != null ? mSplitSelectSource.alreadyRunningTask : null;
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
//根据数据,循环添加child,主要有两种,single,group,其实还有个desktop不知道啥玩意
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
&& groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);
TaskView taskView;
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
//groupTask里有2个,其中一个是需要移除的,那么这个按照单个taskview来处理
taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
} else {
taskView = getTaskViewFromPool(groupTask.taskViewType);
}
//添加到容器
addView(taskView);
//下边就是绑定数据了
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
taskView.bind(groupTask.task2, mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
} else if (isRemovalNeeded) {
// If the task we need to remove is not part of a pair, bind it to the TaskView
// first (to prevent problems), then remove the whole thing.
taskView.bind(groupTask.task1, mOrientationState);
removeView(taskView);
} else if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
groupTask.mSplitBounds);
} else if (taskView instanceof DesktopTaskView) {
((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
// enables instance filtering if the feature flag for it is on
if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
taskView.setUpShowAllInstancesListener();
}
}
//添加clearAll button
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
}
// Keep same previous focused task
TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
// If the list changed, maybe the focused task doesn't exist anymore
if (newFocusedTaskView == null && getTaskViewCount() > 0) {
newFocusedTaskView = getTaskViewAt(0);
}
mFocusedTaskViewId = newFocusedTaskView != null ?
newFocusedTaskView.getTaskViewId() : -1;
//更新taskView的尺寸和方向
updateTaskSize();
updateChildTaskOrientations();
TaskView newRunningTaskView = null;
if (runningTaskId != -1) {
// Update mRunningTaskViewId to be the new TaskView that was assigned by binding
// the full list of tasks to taskViews
newRunningTaskView = getTaskViewByTaskId(runningTaskId);
if (newRunningTaskView != null) {
mRunningTaskViewId = newRunningTaskView.getTaskViewId();
} else {
mRunningTaskViewId = -1;
}
}
int targetPage = -1;
if (mNextPage != INVALID_PAGE) {
// Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
mCurrentPage = previousCurrentPage;
if (currentTaskId != -1) {
currentTaskView = getTaskViewByTaskId(currentTaskId);
if (currentTaskView != null) {
targetPage = indexOfChild(currentTaskView);
}
}
} else {
// Set the current page to the running task, but not if settling on new task.
if (runningTaskId != -1) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
targetPage = indexOfChild(requireTaskViewAt(0));
}
}
if (targetPage != -1 && mCurrentPage != targetPage) {
int finalTargetPage = targetPage;
runOnPageScrollsInitialized(() -> {
setCurrentPage(finalTargetPage);
});
}
if (mIgnoreResetTaskId != -1 &&
getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
mIgnoreResetTaskId = -1;
}
//taskView 属性设置
resetTaskVisuals();
onTaskStackUpdated();
updateEnabledOverlays();
if (isPageScrollsInitialized()) {
onPageScrollsInitialized();
}
}
3.11.onTouchEvent
-
这里主要处理一些按钮的点击事件以及特殊区域的点击事件,其他的滑动都是父类处理
public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); if (showAsGrid()) {//平板模式 int taskCount = getTaskViewCount(); for (int i = 0; i < taskCount; i++) { TaskView taskView = requireTaskViewAt(i); //同样也是判断icon是否处理 if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) { // Keep consuming events to pass to delegate return true; } } } else {//手机模式 TaskView taskView = getCurrentPageTaskView(); //判断taskview是否处理,主要就是上边的logo的点击事件 if (taskView != null && taskView.offerTouchToChildren(ev)) { // Keep consuming events to pass to delegate return true; } } final int x = (int) ev.getX(); final int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_UP: if (mTouchDownToStartHome) { //松手以后,需要回到桌面的, startHome(); } mTouchDownToStartHome = false; break; case MotionEvent.ACTION_CANCEL: mTouchDownToStartHome = false; break; case MotionEvent.ACTION_MOVE: // Passing the touch slop will not allow dismiss to home if (mTouchDownToStartHome && (isHandlingTouch() || squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop)) { //触摸点在需要startHome的区域,可这里移动了一段距离,那么取消startHome标志 mTouchDownToStartHome = false; } break; case MotionEvent.ACTION_DOWN: // Touch down anywhere but the deadzone around the visible clear all button and // between the task views will start home on touch up if (!isHandlingTouch() && !isModal()) {//见补充1 if (mShowEmptyMessage) { //没有recent内容时候,点击以后会进入home页 mTouchDownToStartHome = true; } else { updateDeadZoneRects();//见补充2 //clear all 按钮可见并且在点击区域,说明button消费了点击事件 final boolean clearAllButtonDeadZoneConsumed = mClearAllButton.getAlpha() == 1 && mClearAllButtonDeadZoneRect.contains(x, y); //是否是导航栏区域 final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar && !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) { //非clear all按钮区域,非导航栏区域,非taskview区域,点击以后回到桌面 mTouchDownToStartHome = true; } } } mDownX = x; mDownY = y; break; } return isHandlingTouch(); }
>1.isModal
- 直白点说,就是当前的task是全屏显示还是和其他task一起显示在一个上下文的列表里(recent页面就是这种)
private boolean isModal() {
return mTaskModalness > 0;
}
/**
* How modal is the current task to be displayed, 1 means the task is fully modal and no other
* tasks are show. 0 means the task is displays in context in the list with other tasks.
*/
protected float mTaskModalness = 0;
>2.updateDeadZoneRects
- mClearAllButtonDeadZoneRect就是认为是clear all按钮的点击区域
- mTaskViewDeadZoneRect就是所有taskview的区域,取第一个和最后一个的合集
private void updateDeadZoneRects() {
mClearAllButtonDeadZoneRect.setEmpty();
if (mClearAllButton.getWidth() > 0) {
int verticalMargin = getResources()
.getDimensionPixelSize(R.dimen.recents_clear_all_deadzone_vertical_margin);
mClearAllButton.getHitRect(mClearAllButtonDeadZoneRect);
mClearAllButtonDeadZoneRect.inset(-getPaddingRight() / 2, -verticalMargin);
}
// Get the deadzone rect between the task views
mTaskViewDeadZoneRect.setEmpty();
int count = getTaskViewCount();
if (count > 0) {
final View taskView = requireTaskViewAt(0);
requireTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
taskView.getBottom());
}
}
3.12.PagedView
>1.onTouchEvent
public boolean onTouchEvent(MotionEvent ev) {
if (getChildCount() <= 0) return false;
//速度侦测器更新event
acquireVelocityTrackerAndAddMovement(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//补充2,
updateIsBeingDraggedOnTouchDown(ev);
//正在flinging,中断滚动
if (!mScroller.isFinished()) {
abortScrollerAnimation(false);
}
// Remember where the motion event started
mDownMotionX = ev.getX();
mDownMotionY = ev.getY();
mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
mLastMotionRemainder = 0;
mTotalMotion = 0;
mAllowEasyFling = false;
mActivePointerId = ev.getPointerId(0);
if (mIsBeingDragged) {
pageBeginTransition();
}
break;
case ACTION_MOVE_ALLOW_EASY_FLING:
// Start scrolling immediately
determineScrollingStart(ev);
mAllowEasyFling = true;
break;
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) return true;
float oldScroll = mOrientationHandler.getPrimaryScroll(this);
float dx = ev.getX(pointerIndex);
float dy = ev.getY(pointerIndex);
float direction = mOrientationHandler.getPrimaryValue(dx, dy);
float delta = mLastMotion + mLastMotionRemainder - direction;
int width = getWidth();
int height = getHeight();
int size = mOrientationHandler.getPrimaryValue(width, height);
final float displacement = mOrientationHandler.getSecondaryValue(dx, dy)
/ mOrientationHandler.getSecondaryValue(width, height);
mTotalMotion += Math.abs(delta);
if (mAllowOverScroll) {
float consumed = 0;
if (delta < 0 && mEdgeGlowRight.getDistance() != 0f) {
consumed = size * mEdgeGlowRight.onPullDistance(delta / size, displacement);
} else if (delta > 0 && mEdgeGlowLeft.getDistance() != 0f) {
consumed = -size * mEdgeGlowLeft.onPullDistance(
-delta / size, 1 - displacement);
}
delta -= consumed;
}
delta /= mOrientationHandler.getPrimaryScale(this);
// Only scroll and update mLastMotionX if we have moved some discrete amount. We
// keep the remainder because we are actually testing if we've moved from the last
// scrolled position (which is discrete).
mLastMotion = direction;
int movedDelta = (int) delta;
mLastMotionRemainder = delta - movedDelta;
if (delta != 0) {
mOrientationHandler.setPrimary(this, VIEW_SCROLL_BY, movedDelta);
if (mAllowOverScroll) {
final float pulledToX = oldScroll + delta;
if (pulledToX < mMinScroll) {
mEdgeGlowLeft.onPullDistance(-delta / size, 1.f - displacement);
if (!mEdgeGlowRight.isFinished()) {
mEdgeGlowRight.onRelease();
}
} else if (pulledToX > mMaxScroll) {
mEdgeGlowRight.onPullDistance(delta / size, displacement);
if (!mEdgeGlowLeft.isFinished()) {
mEdgeGlowLeft.onRelease();
}
}
if (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished()) {
postInvalidateOnAnimation();
}
}
} else {
awakenScrollBars();
}
} else {
determineScrollingStart(ev);
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final int activePointerId = mActivePointerId;
final int pointerIndex = ev.findPointerIndex(activePointerId);
if (pointerIndex == -1) return true;
final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev,
pointerIndex);
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker,
mActivePointerId);
float delta = primaryDirection - mDownMotionPrimary;
int pageOrientedSize = (int) (mOrientationHandler.getMeasuredSize(
getPageAt(mCurrentPage))
* mOrientationHandler.getPrimaryScale(this));
boolean isSignificantMove = isSignificantMove(Math.abs(delta), pageOrientedSize);
mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection);
boolean passedSlop = mAllowEasyFling || mTotalMotion > mPageSlop;
boolean isFling = passedSlop && shouldFlingForVelocity(velocity);
boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0;
boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0;
if (DEBUG_FAILED_QUICKSWITCH && !isFling && mAllowEasyFling) {
Log.d("Quickswitch", "isFling=false vel=" + velocity
+ " threshold=" + mEasyFlingThresholdVelocity);
}
if (!mFreeScroll) {
// In the case that the page is moved far to one direction and then is flung
// in the opposite direction, we use a threshold to determine whether we should
// just return to the starting page, or if we should skip one further.
boolean returnToOriginalPage = false;
if (Math.abs(delta) > pageOrientedSize * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
Math.signum(velocity) != Math.signum(delta) && isFling) {
returnToOriginalPage = true;
}
int finalPage;
// We give flings precedence over large moves, which is why we short-circuit our
// test for a large move if a fling has been registered. That is, a large
// move to the left and fling to the right will register as a fling to the right.
if (((isSignificantMove && !isDeltaLeft && !isFling) ||
(isFling && !isVelocityLeft)) && mCurrentPage > 0) {
finalPage = returnToOriginalPage
? mCurrentPage : mCurrentPage - getPanelCount();
snapToPageWithVelocity(finalPage, velocity);
} else if (((isSignificantMove && isDeltaLeft && !isFling) ||
(isFling && isVelocityLeft)) &&
mCurrentPage < getChildCount() - 1) {
finalPage = returnToOriginalPage
? mCurrentPage : mCurrentPage + getPanelCount();
snapToPageWithVelocity(finalPage, velocity);
} else {
snapToDestination();
}
} else {
if (!mScroller.isFinished()) {
abortScrollerAnimation(true);
}
int initialScroll = mOrientationHandler.getPrimaryScroll(this);
int maxScroll = mMaxScroll;
int minScroll = mMinScroll;
if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) ||
((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) {
mScroller.springBack(initialScroll, 0, minScroll, maxScroll, 0, 0);
mNextPage = getDestinationPage();
} else {
int velocity1 = -velocity;
// Continue a scroll or fling in progress
mScroller.fling(initialScroll, 0, velocity1, 0, minScroll, maxScroll, 0, 0,
Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR), 0);
int finalPos = mScroller.getFinalX();
mNextPage = getDestinationPage(finalPos);
onNotSnappingToPageInFreeScroll();
}
invalidate();
}
}
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
// End any intermediate reordering states
resetTouchState();
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
snapToDestination();
}
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
releaseVelocityTracker();
break;
}
return true;
}
>2.updateIsBeingDraggedOnTouchDown
更新是否拖动中
protected void updateIsBeingDraggedOnTouchDown(MotionEvent ev) {
// mScroller.isFinished should be false when being flinged.
final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mPageSlop / 3);
if (finishedScrolling) {
//非滚动状态
mIsBeingDragged = false;
if (!mScroller.isFinished() && !mFreeScroll) {
setCurrentPage(getNextPage());
pageEndTransition();
}
mIsBeingDragged = !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished();
} else {
mIsBeingDragged = true;
}
// Catch the edge effect if it is active.
float displacement = mOrientationHandler.getSecondaryValue(ev.getX(), ev.getY())
/ mOrientationHandler.getSecondaryValue(getWidth(), getHeight());
if (!mEdgeGlowLeft.isFinished()) {
mEdgeGlowLeft.onPullDistance(0f, 1f - displacement);
}
if (!mEdgeGlowRight.isFinished()) {
mEdgeGlowRight.onPullDistance(0f, displacement);
}
}
4.RecentsModel.java
参考3.9.2,这个类是用来获取recent的数据的
public class RecentsModel implements IconChangeListener, TaskStackChangeListener,
TaskVisualsChangeListener {
4.1.构造方法
private RecentsModel(Context context) {
mContext = context;
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
SystemUiProxy.INSTANCE.get(context));
//注册3个监听
IconProvider iconProvider = new IconProvider(context);
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
mIconCache.registerTaskVisualsChangeListener(this);
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
}
4.2.onTaskStackChangedBackground
public void onTaskStackChangedBackground() {
if (!mThumbnailCache.isPreloadingEnabled()) {
// Skip if we aren't preloading
return;
}
int currentUserId = Process.myUserHandle().getIdentifier();
if (!checkCurrentOrManagedUserId(currentUserId, mContext)) {
// Skip if we are not the current user
return;
}
// Keep the cache up to date with the latest thumbnails
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
int runningTaskId = runningTask != null ? runningTask.id : -1;
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), taskGroups -> {
for (GroupTask group : taskGroups) {
if (group.containsTask(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(group.task1);
mThumbnailCache.updateThumbnailInCache(group.task2);
}
});
}
4.3.isTaskListValid
task集合数据变化,changeId也会变化,所以根据这个判断数据是否发生了变化
public boolean isTaskListValid(int changeId) {
return mTaskList.isTaskListValid(changeId);
}
4.4.getTasks
public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
}
5.RecentTasksList
5.1.构造方法
public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keyguardManager,
SystemUiProxy sysUiProxy) {
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
mSysUiProxy = sysUiProxy;
//注册recent task的改变监听
sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
@Override
public void onRecentTasksChanged() throws RemoteException {//补充1
mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
}
@Override
public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
mMainThreadExecutor.execute(() -> {
RecentTasksList.this.onRunningTaskAppeared(taskInfo);//补充2
});
}
@Override
public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
mMainThreadExecutor.execute(() -> {
RecentTasksList.this.onRunningTaskVanished(taskInfo);//补充3
});
}
});
//初始化数据
initRunningTasks(mSysUiProxy.getRunningTasks(Integer.MAX_VALUE));
}
>1.onRecentTasksChanged
tasks发生变化,使当前数据标志为无效
public void onRecentTasksChanged() {
invalidateLoadedTasks();
}
private synchronized void invalidateLoadedTasks() {
UI_HELPER_EXECUTOR.execute(() -> mResultsBg = INVALID_RESULT);
mResultsUi = INVALID_RESULT;
mChangeId++;
}
>2.onRunningTaskAppeared
有新的task出现,加入集合
private void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
if (taskInfo.taskId == existingTask.taskId) {
return;
}
}
mRunningTasks.add(taskInfo);
if (mRunningTasksListener != null) {
mRunningTasksListener.onRunningTasksChanged();
}
}
>3.onRunningTaskVanished
有task被销毁了,从集合里移除
private void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
if (existingTask.taskId != taskInfo.taskId) continue;
mRunningTasks.remove(existingTask);
if (mRunningTasksListener != null) {
mRunningTasksListener.onRunningTasksChanged();
}
return;
}
}
5.2.getTaskKeys
public void getTaskKeys(int numTasks, Consumer<ArrayList<GroupTask>> callback) {
// Kick off task loading in the background
UI_HELPER_EXECUTOR.execute(() -> {
ArrayList<GroupTask> tasks = loadTasksInBackground(numTasks, -1,
true /* loadKeysOnly */);
mMainThreadExecutor.execute(() -> callback.accept(tasks));
});
}
>1.loadTasksInBackground
TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
//数据还是mSysUiProxy获取的
ArrayList<GroupedRecentTaskInfo> rawTasks =
mSysUiProxy.getRecentTasks(numTasks, currentUserId);
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(rawTasks);
SparseBooleanArray tmpLockedUsers = new SparseBooleanArray() {
@Override
public boolean get(int key) {
if (indexOfKey(key) < 0) {
// Fill the cached locked state as we fetch
put(key, mKeyguardManager.isDeviceLocked(key));
}
return super.get(key);
}
};
//下边主要是数据的处理,根据loadKeysOnly是否为true,task只保留主键数据
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
//原始的数据转化为recent用的GroupTask
for (GroupedRecentTaskInfo rawTask : rawTasks) {
//.
ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
ActivityManager.RecentTaskInfo taskInfo2 = rawTask.getTaskInfo2();
Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
Task task1 = loadKeysOnly
? new Task(task1Key)
: Task.from(task1Key, taskInfo1,
tmpLockedUsers.get(task1Key.userId) /* isLocked */);
task1.setLastSnapshotData(taskInfo1);
Task task2 = null;
if (taskInfo2 != null) {
Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
task2 = loadKeysOnly
? new Task(task2Key)
: Task.from(task2Key, taskInfo2,
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
task2.setLastSnapshotData(taskInfo2);
}
final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
convertSplitBounds(rawTask.getSplitBounds());
allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
}
return allTasks;
}
5.3.getTasks
public synchronized int getTasks(boolean loadKeysOnly,
Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
final int requestLoadId = mChangeId;
//当前数据是有效的
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
//
if (callback != null) {
//过滤,收集mResultsUi里的数据
ArrayList<GroupTask> result = mResultsUi.stream().filter(filter)
.map(GroupTask::copy)
.collect(Collectors.toCollection(ArrayList<GroupTask>::new));
mMainThreadExecutor.post(() -> {
callback.accept(result);//数据给回调
});
}
return requestLoadId;
}
//后台启动任务加载
mLoadingTasksInBackground = true;
UI_HELPER_EXECUTOR.execute(() -> {
//后台数据无效,重新获取
if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) {
mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly);
}
TaskLoadResult loadResult = mResultsBg;
mMainThreadExecutor.execute(() -> {
//更新数据
mLoadingTasksInBackground = false;
mResultsUi = loadResult;
if (callback != null) {
//过滤回调数据
ArrayList<GroupTask> result = mResultsUi.stream().filter(filter)
.map(GroupTask::copy)
.collect(Collectors.toCollection(ArrayList<GroupTask>::new));
callback.accept(result);
}
});
});
return requestLoadId;
}
6.SystemUiProxy.java
单例模式
6.1.setProxy
- mRecentTasks就是RecentTasksController.java里的静态内部类IRecentTasksImpl,具体数据获取查看这个类
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
IBackAnimation backAnimation, IDesktopMode desktopMode) {
//.
//注册各种监听
if (mPipAnimationListener != null && mPip != null) {
setPipAnimationListener(mPipAnimationListener);
}
if (mSplitScreenListener != null && mSplitScreen != null) {
registerSplitScreenListener(mSplitScreenListener);
}
if (mStartingWindowListener != null && mStartingWindow != null) {
setStartingWindowListener(mStartingWindowListener);
}
if (mSysuiUnlockAnimationController != null && mLauncherUnlockAnimationController != null) {
setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
}
new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
if (mRecentTasksListener != null && mRecentTasks != null) {
registerRecentTasksListener(mRecentTasksListener);
}
if (mBackAnimation != null && mBackToLauncherCallback != null) {
setBackToLauncherCallback(mBackToLauncherCallback);
}
}
代理设置见补充1
>1.TouchInteractionService.java
public class TISBinder extends IOverviewProxy.Stub {
public void onInitialize(Bundle bundle) {
//..
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
splitscreen, onehanded, shellTransitions, startingWindow,
recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode);
TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
preloadOverview(true /* fromInit */);
});
6.2.registerRecentTasksListener
交给代理注册监听
public void registerRecentTasksListener(IRecentTasksListener listener) {
if (mRecentTasks != null) {
try {
mRecentTasks.registerRecentTasksListener(listener);
}
}
mRecentTasksListener = listener;
}
6.3.getRunningTasks
public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
if (mRecentTasks != null
&& mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
try {
return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
}
}
return new ArrayList<>();
}
7.TaskView
public class TaskView extends FrameLayout implements Reusable {
>1.task.xml
- 这个就是recent里边显示的控件了,加载的布局是这个
- 3.1构造方法,taskViewPool里创建taskView的时候用到这个布局
<com.android.quickstep.views.TaskView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:defaultFocusHighlightEnabled="false"
android:focusable="true">
<com.android.quickstep.views.TaskThumbnailView
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/show_windows"
android:layout_height="@dimen/recents_filter_icon_size"
android:layout_width="@dimen/recents_filter_icon_size"
android:layout_gravity="end"
android:alpha="0"
android:tint="@color/recents_filter_icon"
android:contentDescription="@string/recents_filter_icon_desc"
android:importantForAccessibility="no"
android:src="@drawable/ic_select_windows" />
<com.android.quickstep.views.IconView
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
android:focusable="false"
android:importantForAccessibility="no"/>
</com.android.quickstep.views.TaskView>
7.1.onClick
private void onClick(View view) {
if (getTask() == null) {
return;
}
if (confirmSecondSplitSelectApp()) {
return;
}
//就是启动点击的app
launchTasks();
}
>1.confirmSecondSplitSelectApp
当前是否是split模式,正在选择第二个应用。
protected boolean confirmSecondSplitSelectApp() {
int index = getLastSelectedChildTaskIndex();
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
if (container != null) {
return getRecentsView().confirmSplitSelect(this, container.getTask(),
container.getIconView().getDrawable(), container.getThumbnailView(),
container.getThumbnailView().getThumbnail(), /* intent */ null);
}
return false;
}
7.2.bind
这个就是绑定数据了,3.10里有用到
public void bind(Task task, RecentsOrientedState orientedState) {
cancelPendingLoadTasks();
mTask = task;
mTaskIdContainer[0] = mTask.key.id;
mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
mIconView, STAGE_POSITION_UNDEFINED);
//缩略图绑定数据
mSnapshotView.bind(task);
//更新icon和缩略图的布局
setOrientationState(orientedState);
}
>1.setOrientationState
public void setOrientationState(RecentsOrientedState orientationState) {
PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
boolean isGridTask = isGridTask();
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
int taskMargin = deviceProfile.overviewTaskMarginPx;
//根据当前的handler设置mIconView的布局参数
orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
thumbnailTopMargin, isRtl);
iconParams.width = iconParams.height = taskIconHeight;
mIconView.setLayoutParams(iconParams);
mIconView.setRotation(orientationHandler.getDegreesRotated());
int iconDrawableSize = isGridTask ? deviceProfile.overviewTaskIconDrawableSizeGridPx
: deviceProfile.overviewTaskIconDrawableSizePx;
mIconView.setDrawableSize(iconDrawableSize, iconDrawableSize);
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
//快照就设置个topMargin,也就是显示在icon下边
snapshotParams.topMargin = thumbnailTopMargin;
mSnapshotView.setLayoutParams(snapshotParams);
mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
mDigitalWellBeingToast.initialize(mTask);
}
7.3.onTaskListVisibilityChanged
可见性发生变化的时候,会更新icon以及thumbnai
public void onTaskListVisibilityChanged(boolean visible) {
onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL);
}
public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
if (mTask == null) {
return;
}
cancelPendingLoadTasks();
if (visible) {//可见
RecentsModel model = RecentsModel.INSTANCE.get(getContext());
TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
TaskIconCache iconCache = model.getIconCache();
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
//更新thumbail
mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
mTask, thumbnail -> {
mSnapshotView.setThumbnail(mTask, thumbnail);
});
}
if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
//更新icon
mIconLoadRequest = iconCache.updateIconInBackground(mTask,
(task) -> {
setIcon(mIconView, task.icon);
mDigitalWellBeingToast.initialize(mTask);
});
}
} else {//不可见
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
//设置为空
mSnapshotView.setThumbnail(null, null);
mTask.thumbnail = null;
}
if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
setIcon(mIconView, null);
}
}
}
7.4.TaskThumbnailView
public class TaskThumbnailView extends View {
>1.setThumbnail
public void setThumbnail(@Nullable Task task, @Nullable ThumbnailData thumbnailData,
boolean refreshNow) {
mTask = task;
boolean thumbnailWasNull = mThumbnailData == null;
mThumbnailData =
(thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
if (mTask != null) {
updateSplashView(mTask.icon);
}
if (refreshNow) {
refresh(thumbnailWasNull && mThumbnailData != null);
}
}
7.5.GroupedTaskView
就是一个屏幕同时显示2个app的情况。 我们可以在recents列表里,点击某个app上边的图标,有个弹框,选择split top,完事这个app就显示在顶部了,这时候再选一个要同时显示的app,在其缩略图上点一下,就能看到这种效果了。
public class GroupedTaskView extends TaskView {
>1.task_group.xml
可以看到,有2个用来显示icon,2个用来显示缩略图
<com.android.quickstep.views.GroupedTaskView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:defaultFocusHighlightEnabled="false"
android:focusable="true">
<com.android.quickstep.views.TaskThumbnailView
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.android.quickstep.views.TaskThumbnailView
android:id="@+id/bottomright_snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/show_windows"/>
<ImageView
android:id="@+id/show_windows_right" />
<com.android.quickstep.views.IconView
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
android:focusable="false"
android:importantForAccessibility="no"/>
<com.android.quickstep.views.IconView
android:id="@+id/bottomRight_icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
android:focusable="false"
android:importantForAccessibility="no"/>
</com.android.quickstep.views.GroupedTaskView>
8.recent启动的流程
8.1.手机模式
- 在03NavigationBar的4.7小节的recents点击事件里有讲到 最终的事件处理是交给一个服务了,再简单贴下相关的代码
- 手机模式的导航栏是在SystemUi里的
>1.OverviewProxyService.java
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
private void internalConnectToCurrentUser() {
//...见8.3
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
try {
mBound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
UserHandle.of(mUserTracker.getUserId()));
} catch (SecurityException e) {
//...
}
8.2.平板模式
-
平板模式,导航功能叫taskbar,是集成在launcher3里的,基本都在taskbar的目录下。
-
平板模式,recent按钮的点击事件如下,TaskbarNavButtonController.java
private void navigateToOverview() { if (mScreenPinned) { return; } TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); //helper类来源见8.3.3实例化,方法见见8.4.1 mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE); }
8.3.TouchInteractionService.java
>1.TouchInteractionService
注册action如下
<service android:name="com.android.quickstep.TouchInteractionService"
android:permission="android.permission.STATUS_BAR_SERVICE"
android:directBootAware="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.QUICKSTEP_SERVICE"/>
</intent-filter>
</service>
>2.onOverviewToggle
private final TISBinder mTISBinder = new TISBinder();
public class TISBinder extends IOverviewProxy.Stub {
//...
//这个就是recents按钮点击以后调用的代码
public void onOverviewToggle() {
//...
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
//见8.4.1
mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
}
>3.onUserUnlocked
解锁屏幕以后的回调
public void onUserUnlocked() {
mTaskAnimationManager = new TaskAnimationManager(this);
//oberver类
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
//初始化了helper类
mOverviewCommandHelper = new OverviewCommandHelper(this,
mOverviewComponentObserver, mTaskAnimationManager);
mResetGestureInputConsumer = new ResetGestureInputConsumer(
mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();
onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
onAssistantVisibilityChanged();
// Initialize the task tracker
TopTaskTracker.INSTANCE.get(this);
// Temporarily disable model preload
// new ModelPreload().start(this);
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
}
8.4.OverviewCommandHelper.java
>1.addCommand
加入集合
public void addCommand(int type) {
if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
//队列里最多可以存3个
return;
}
CommandInfo cmd = new CommandInfo(type);
MAIN_EXECUTOR.execute(() -> addCommand(cmd));
}
加入之前集合是空的,那么添加完就开始执行,否则等上一次的完事后再开始执行
private void addCommand(CommandInfo cmd) {
boolean wasEmpty = mPendingCommands.isEmpty();
mPendingCommands.add(cmd);
if (wasEmpty) {
executeNext();
}
}
//executeNext
private void executeNext() {
if (mPendingCommands.isEmpty()) {
return;
}
CommandInfo cmd = mPendingCommands.get(0);
if (executeCommand(cmd)) {//补充2
scheduleNextTask(cmd);
}
}
>2.executeCommand
- 从桌面或者其他应用点击recent按钮,recents是空
- 已经在recent页面了,点击recent按钮,recents不是空
private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
BaseActivityInterface<?, T> activityInterface =
mOverviewComponentObserver.getActivityInterface();
//只有overview在前台的话不为null,见补充3
RecentsView recents = activityInterface.getVisibleRecentsView();
//这里只分析下点击recents按钮的逻辑,其他type不研究
if (recents == null) {
//..
} else {
switch (cmd.type) {
//..
case TYPE_TOGGLE://recents已经显示的情况,再点击就会执行这个
return launchTask(recents, getNextTask(recents), cmd);//见补充4,5
case TYPE_HOME:
recents.startHome();
return true;
}
}
//第一种情况,launcher在前台,这里基本会返回true,见8.5
if (activityInterface.switchToRecentsIfVisible(completeCallback)) {
// If successfully switched, wait until animation finishes
return false;
}
//第二种,launcher不在前台,上边的if结果是false,会继续往下走
final T activity = activityInterface.getCreatedActivity();
if (activity != null) {
InteractionJankMonitorWrapper.begin(
activity.getRootView(),
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
}
//如果是launcher3,返回的就是launcherHandler,如果是第三方的,返回的就是fallbackHandler
AbsSwipeUpHandler interactionHandler = mService.getSwipeUpHandlerFactory()
.newHandler(gestureState, cmd.createTime);
//..
//最终一般会走else这里,启动intent,见8.6
} else {//见8.6
Intent intent = new Intent(interactionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
//见8.7
cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
gestureState, intent, interactionHandler);
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
cmd.mActiveCallbacks.addListener(recentAnimListener);
}
>3.activityInterface
这里贴下activityinterface的代码,
mActivityInterface = LauncherActivityInterface.INSTANCE;
//LauncherActivityInterface.java
@Override
public RecentsView getVisibleRecentsView() {
Launcher launcher = getVisibleLauncher();
//非overview状态的话返回null,overview状态就返回对应的recentView
RecentsView recentsView =
launcher != null && launcher.getStateManager().getState().overviewUi
? launcher.getOverviewPanel() : null;
if (recentsView == null || (!launcher.hasBeenResumed()
&& recentsView.getRunningTaskViewId() == -1)) {
// If live tile has ended, return null.
return null;
}
return recentsView;
}
>4.launchTask
recentView显示的时候,再次点击recents按钮,就会打开某个历史app,具体打开哪个看下边的逻辑代码
private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
RunnableList callbackList = null;
if (taskView != null) {
taskView.setEndQuickswitchCuj(true);
//用动画启动当前任务
callbackList = taskView.launchTasks();
}
if (callbackList != null) {
callbackList.add(() -> scheduleNextTask(cmd));
return false;
} else {
//可以看到taskView为null的话,就直接显示桌面了。
recents.startHome();
return true;
}
}
>5.getNextTask
private TaskView getNextTask(RecentsView view) {
//先查找runningTaskView
final TaskView runningTaskView = view.getRunningTaskView();
if (runningTaskView == null) {
//当前活动的taskView为空的话直接返回第一个
return view.getTaskViewAt(0);
} else {
//活动的taskview不为空,taskid加1查找nextTaskView,
final TaskView nextTask = view.getNextTaskView();
//nextTask不为空,返回,为空就还用当前活动的taskview
return nextTask != null ? nextTask : runningTaskView;
}
}
8.5.情况一launcher在前台
- 测试步骤: 点击home键回到桌面,然后点击recents按钮
- goToState里会会调listener里的方法,我们的RecentsView里就有添加listener,所以会做出对应的操作,显示或者隐藏自己。
//LauncherActivityInterface.java
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
Launcher launcher = getVisibleLauncher();
if (launcher == null) {
return false;//在前台的话,这里不会为null的,所以会走到下边
}
closeOverlay();
//会走这里,切换状态指overview
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(),
onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
return true;
}
8.6.情况二launcher在后台
- 测试步骤:随便打开一个app,然后点击recents按钮。
>1.getLaunchIntent如下
private static Intent createHomeIntent() {
return new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
>2.log
Intent { act=android.intent.action.MAIN
cat=[android.intent.category.HOME]
flg=0x10000000
pkg=com.android.launcher3 cmp=com.android.launcher3/.uioverrides.QuickstepLauncher
(has extras) }
>3.清单文件
对应的是launcher3/quickstep目录下的AndroidManifest-launcher.xml
<activity
android:name="com.android.launcher3.uioverrides.QuickstepLauncher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SHOW_WORK_APPS" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
8.7.startRecentsAnimation
TaskAnimationManager.java
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
// But force-finish it anyways,结束旧的动画
finishRunningRecentsAnimation(false /* toHome */);
if (mCallbacks != null) {
//清除旧的回调
cleanUpRecentsAnimation();
}
final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
mLastGestureState = gestureState;
//见补充1
mCallbacks = new RecentsAnimationCallbacks(SystemUiProxy.INSTANCE.get(mCtx),
activityInterface.allowMinimizeSplitScreen());
//添加一堆回调
mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
activityInterface.runOnInitBackgroundStateUI(() ->
interactionHandler.onGestureEnded(0, new PointF(), new PointF()));
cmd.removeListener(this);
}
//...
mCallbacks.addListener(gestureState);
mCallbacks.addListener(listener);
//...
} else {//见补充2
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, eventTime, mCallbacks, null, null));
}
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
return mCallbacks;
}
>1.RecentsAnimationCallbacks
补充2里的回调会调用这个方法,这个方法里可以看到,最终主要是调用listener的方法
public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
RemoteAnimationTarget[] appTargets,
RemoteAnimationTarget[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
mController = new RecentsAnimationController(animationController,
mAllowMinimizeSplitScreen, this::onAnimationFinished);
if (mCancelled) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
mController::finishAnimationToApp);
} else {
RemoteAnimationTarget[] nonAppTargets =
mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
if (nonAppTargets == null) {
nonAppTargets = new RemoteAnimationTarget[0];
}
final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
for (RecentsAnimationListener listener : getListeners()) {
//回调
listener.onRecentsAnimationStart(mController, targets);
}
});
}
}
>2.ActivityManagerWrapper.java
public boolean startRecentsActivity(
Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
try {
IRecentsAnimationRunner runner = null;
if (animationHandler != null) {
runner = new IRecentsAnimationRunner.Stub() {
@Override
public void onAnimationStart(IRecentsAnimationController controller,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
Rect homeContentInsets, Rect minimizedHomeBounds) {
final RecentsAnimationControllerCompat controllerCompat =
new RecentsAnimationControllerCompat(controller);
//见补充1
animationHandler.onAnimationStart(controllerCompat, apps,
wallpapers, homeContentInsets, minimizedHomeBounds);
}
@Override
public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
animationHandler.onAnimationCanceled(
ThumbnailData.wrap(taskIds, taskSnapshots));
}
@Override
public void onTasksAppeared(RemoteAnimationTarget[] apps) {
animationHandler.onTasksAppeared(apps);
}
};
}//这里的service就是ActivityTaskManagerService,见补充3
getService().startRecentsActivity(intent, eventTime, runner);
return true;
} catch (Exception e) {
return false;
}
}
>3.ActivityTaskManagerService.java
public void startRecentsActivity(Intent intent, long eventTime,
@Nullable IRecentsAnimationRunner recentsAnimationRunner) {
enforceTaskPermission("startRecentsActivity()");
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
final String recentsFeatureId = mRecentTasks.getRecentsComponentFeatureId();
final int recentsUid = mRecentTasks.getRecentsComponentUid();
final WindowProcessController caller = getProcessController(callingPid, callingUid);
//
final RecentsAnimation anim = new RecentsAnimation(this, mTaskSupervisor,
getActivityStartController(), mWindowManager, intent, recentsComponent,
recentsFeatureId, recentsUid, caller);
if (recentsAnimationRunner == null) {
} else {//见补充4
anim.startRecentsActivity(recentsAnimationRunner, eventTime);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
>4.RecentsAnimation.java
void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) {
//...
// If the activity is associated with the root recents task, then try and get that first
Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetRootTask);
final boolean hasExistingActivity = targetActivity != null;
if (hasExistingActivity) {
mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask);
if (mRestoreTargetBehindRootTask == null
&& targetRootTask.getTopMostTask() == targetActivity.getTask()) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
return;
}
}
if (targetActivity == null || !targetActivity.isVisibleRequested()) {
mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(
true /* forceSend */, targetActivity);
}
final LaunchingState launchingState =
mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
setProcessAnimating(true);
mService.deferWindowLayout(); // 延迟窗口布局
try {
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask);
//...
} else {
//...
}
targetActivity.mLaunchTaskBehind = true;
mLaunchedTargetActivity = targetActivity;
targetActivity.intent.replaceExtras(mTargetIntent);
// 补充5
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
this, mDefaultTaskDisplayArea.getDisplayId(),
mTaskSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity);
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
//..
// Register for root task order changes
mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this);
} catch (Exception e) {
} finally {
mService.continueWindowLayout();//恢复窗口布局
}
}
>5.initializeRecentsAnimation
void initializeRecentsAnimation(int targetActivityType,
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) {
mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
callbacks, displayId);
mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity);
}
//RecentsAnimationController
public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds,
ActivityRecord targetActivity) {
mTargetActivityType = targetActivityType;
mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
.getVisibleTasks();
final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
.getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
if (targetRootTask != null) {
final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
{ if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
visibleTasks);
targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
final int taskCount = visibleTasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
final Task task = visibleTasks.get(i);
if (skipAnimation(task)) {
continue;
}
//见补充6
addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
(type, anim) -> task.forAllWindows(win -> {
win.onAnimationFinished(type, anim);
}, true /* traverseTopToBottom */));
}
// Skip the animation if there is nothing to animate
if (mPendingAnimations.isEmpty()) {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
return;
}
try {
linkToDeathOfRunner();
} catch (RemoteException e) {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
return;
}
attachNavigationBarToApp();
// Adjust the wallpaper visibility for the showing target activity
mTargetActivityRecord = targetActivity;
if (targetActivity.windowsCanBeWallpaperTarget()) {
mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
mDisplayContent.setLayoutNeeded();
}
mService.mWindowPlacerLocked.performSurfacePlacement();
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);
// Notify that the animation has started
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(true /* running */);
}
}
>6.RecentsAnimationController.java
TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
OnAnimationFinishedCallback finishedCallback) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
isRecentTaskInvisible);
//----- 补充7
task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden,
ANIMATION_TYPE_RECENTS, finishedCallback);
task.commitPendingTransaction();
mPendingAnimations.add(taskAdapter);
return taskAdapter;
}
记录下常见的task的类型
public static String activityTypeToString(@ActivityType int applicationType) {
switch (applicationType) {
case ACTIVITY_TYPE_UNDEFINED: return "undefined";//0
case ACTIVITY_TYPE_STANDARD: return "standard";//1
case ACTIVITY_TYPE_HOME: return "home";//2
case ACTIVITY_TYPE_RECENTS: return "recents";//3
case ACTIVITY_TYPE_ASSISTANT: return "assistant";//4
case ACTIVITY_TYPE_DREAM: return "dream";//5
}
return String.valueOf(applicationType);
}
>7.startAnimation
Task类的父类的父类 WindowContainer.java
protected final SurfaceAnimator mSurfaceAnimator;
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
SurfaceAnimator里有用到SurfaceController类,这个看起来像是我们要的东西,不细究了,太复杂了。
11. 总结
- 主要介绍了,我们点击recents按钮以后launcher3里边服务如何启动对应的页面
- recent view的布局结构
- 自定义的RecentsView逻辑,监听launcher的状态变化,隐藏显示自己
- 子控件TaskView
- FallbackRecentsView 是给第三方launcher用的,LauncherRecentsView 是给launcher3用的