关注个人简介,技术不迷路~
1.简介
这里主要介绍下点击导航栏的recents按钮,跳转的页面。我以前以为这东西是个单独的app,看了代码才发现,它是launcher的一部分。我们打开源码/packages/apps/Launcher3 目录下,发现除了res,src,清单文件外,还有2个目录,go目录是给低配置的机器用的简化版的代码,quickstep是我们要看的东西。
2.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 \
3.layout
底层布局还是上一篇里用到launcher.xml,里边有个这样的include,在默认的res下是空的,在quickstep里重写了
<include
android:id="@+id/overview_panel"
layout="@layout/overview_panel" />
</com.android.launcher3.dragndrop.DragLayer>
上图的1和2分别是下边的3.1和3.2布局
3.1.overview_panel.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<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>
3.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" />
<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>
4.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";
}
}
5.LauncherRecentsView
点击recents按钮显示的页面就是这个容器了,这里通过在构造方法里添加监听的方法,获取state状态,同步修改自己的可见性。
public class LauncherRecentsView extends RecentsView<QuickstepLauncher, LauncherState>
implements StateListener<LauncherState> {
public LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
//这里添加了state改变的监听
mActivity.getStateManager().addStateListener(this);
}
public void startHome() {
//home页就是normal状态
mActivity.getStateManager().goToState(NORMAL);
AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
//这个就是回调,状态改变开始
public void onStateTransitionStart(LauncherState toState) {
setOverviewStateEnabled(toState.overviewUi);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
}
setFreezeViewVisibility(true);
}
//这个就是回调,状态改变结束
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
reset();
}
//点击recent的话状态就是overview
boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
setFreezeViewVisibility(false);
if (finalState != OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(false);
}
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
}
6.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 {
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();
//...
}
6.1.init
初始化action view
public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
mActionsView = actionsView;
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
mSplitSelectStateController = splitController;
}
QuickstepLauncher.java里会调用
protected void setupViews() {
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
RecentsView overviewPanel = getOverviewPanel();
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController(), getStatsLogManager());
//这调用
overviewPanel.init(mActionsView, mSplitSelectStateController);
//..
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
6.2.onAttachedToWindow
主要是添加一堆监听
protected void onAttachedToWindow() {
super.onAttachedToWindow();
updateTaskStackListenerState();
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();
}
6.3.maybeDrawEmptyMessage
public void draw(Canvas canvas) {
maybeDrawEmptyMessage(canvas);
super.draw(canvas);
}
//
protected void maybeDrawEmptyMessage(Canvas canvas) {
if (mShowEmptyMessage && mEmptyTextLayout != null) {
// Offset to center in the visible (non-padded) part of RecentsView
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();
}
}
6.4.taskView可见性
看效果图可以知道,最多可以显示3个taskView,中间一个完整显示的,以及两边部分显示的。
public boolean isTaskViewVisible(TaskView tv) {
if (showAsGrid()) {
int screenStart = mOrientationHandler.getPrimaryScroll(this);
int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
return isTaskViewWithinBounds(tv, screenStart, screenEnd);
} else {
// For now, just check if it's the active task or an adjacent task
return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
}
}
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();
}
}
6.5.resetTaskVisuals
重新设置下taskView的属性,在重新add child以后会执行
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);
}
}
// resetTaskVisuals is called at the end of dismiss animation which could update
// primary and secondary translation of the live tile cut out. We will need to do so
// here accordingly.
runActionOnRemoteHandles(remoteTargetHandle -> {
TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
simulator.taskPrimaryTranslation.value = 0;
simulator.taskSecondaryTranslation.value = 0;
simulator.fullScreenProgress.value = 0;
simulator.recentsViewScale.value = 1;
});
// Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
// null.
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);
}
6.6.setVisibility
设置自己的可见性,并同步修改action view的可见性
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);
}
}
}
>调用的地方
-
设置透明度,为0的时候就不可见了。
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); } }
初始化的时候透明度为0,也就是默认启动的时候是不可见的
public void init(OverviewActionsView actionsView,
SplitSelectStateController splitPlaceholderView) {
super.init(actionsView, splitPlaceholderView);
setContentAlpha(0);
}
上边的方法是在QuickstepLauncher.java的setupViews的方法里调用的
overviewPanel.init(mActionsView, mSplitSelectStateController);
-
下边这个方法是动画开始的时候会冻结view的可见性,结束的时候会恢复为false
public void setFreezeViewVisibility(boolean freezeViewVisibility) { if (mFreezeViewVisibility != freezeViewVisibility) { mFreezeViewVisibility = freezeViewVisibility; if (!mFreezeViewVisibility) { setVisibility(mContentAlpha > 0 ? VISIBLE : INVISIBLE); } } }
>透明度的改变
前边有讲过,launcher的view显示有10种状态,我们也简单分析了几种常见的。overview的可见性也是通过改变state来实现的。有专门的StateManager类来修改state,还有state的处理类。这里简单贴下非动画的改变状态的情况,动画的麻烦,还有listener,就不看了。
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);
//透明度就是这个了。overview状态,透明度为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);
}
RecentsViewStateController.java
FloatProperty<RecentsView> getContentAlphaProperty() {
return CONTENT_ALPHA;
}
//看下set方法,可以看到,就是设置recentsView的透明度,上边有贴代码,透明度为0就不可见了。
public static final FloatProperty<RecentsView> CONTENT_ALPHA =
new FloatProperty<RecentsView>("contentAlpha") {
@Override
public void setValue(RecentsView view, float v) {
view.setContentAlpha(v);
}
@Override
public Float get(RecentsView view) {
return view.getContentAlpha();
}
};
6.7.数据的获取
state改变的回调哪里调用的后边再分析。
>onStateTransitionStart
public void onStateTransitionStart(LauncherState toState) {
//overview状态的话这个为true
setOverviewStateEnabled(toState.overviewUi);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
}
setFreezeViewVisibility(true);
}
>setOverviewStateEnabled
overview状态的话这个为true,非overview的时候又成了false
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
//更新数据
updateTaskStackListenerState();
mOrientationState.setRotationWatcherEnabled(enabled);
if (!enabled) {
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTasks = null;
mSplitBoundsConfig = null;
}
updateLocusId();
}
>updateTaskStackListenerState
mHandleTaskStackChanges默认是false的,当我们点击recent以后就要显示overview了,这时候handleTaskStackChanges为true,后边几个条件都是true的。当退出overview的时候这里的值又成了false,下次再次进入overview的时候又成了true
private void updateTaskStackListenerState() {
boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow()
&& getWindowVisibility() == VISIBLE;
if (handleTaskStackChanges != mHandleTaskStackChanges) {
mHandleTaskStackChanges = handleTaskStackChanges;
if (handleTaskStackChanges) {
reloadIfNeeded();
}
}
}
//reloadIfNeeded调用的是TaskList的方法获取数据
public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
}
>reloadIfNeeded
这个方法在每次overview显示的时候都会调用,
public void reloadIfNeeded() {
if (!mModel.isTaskListValid(mTaskListChangeId)) {
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
.getFilter(mFilterState.getPackageNameToFilter()));
}
}
#onStateTransitionComplete
可以看到,每次退出overview回到normal状态的时候,会调用reset
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
}
#reset
id又改成-1了
public void reset() {
setCurrentTask(-1);
mCurrentPageScrollDiff = 0;
mIgnoreResetTaskId = -1;
mTaskListChangeId = -1;
mFocusedTaskViewId = -1;
6.8.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();
// With all tasks removed, touch handling in PagedView is disabled and we need to reset
// touch state or otherwise values will be obsolete.
resetTouchState();
if (isPageScrollsInitialized()) {
onPageScrollsInitialized();
}
return;
}
//..中间一堆代码就是获取容器旧的状态,方便后边恢复,因为下边会移除所有的child
removeAllViews();
//..
// 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()) {
// If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
// to be a temporary container for the remaining task.
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;
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();
}
}
7.TaskView
这个就是recent里边显示的控件了,加载的布局是这个
7.1.task.xml
<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.2.TaskView
public class TaskView extends FrameLayout implements Reusable {
>onClick
private void onClick(View view) {
if (getTask() == null) {
return;
}
if (confirmSecondSplitSelectApp()) {
return;
}
launchTasks();
}
>bind
这个就是绑定数据了,6.8里有用到
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);
setOrientationState(orientedState);
}
8.GroupedTaskView
就是一个屏幕同时显示2个app的情况。 我们可以在recents列表里,点击某个app上边的图标,有个弹框,选择split top,完事这个app就显示在顶部了,这时候再选一个要同时显示的app,在其缩略图上点一下,就能看到这种效果了。
public class GroupedTaskView extends TaskView {
[图片上传失败...(image-94666b-1687158554079)]
8.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>
9.RecentsModel.java
private RecentsModel(Context context) {
mContext = context;
//app列表
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
SystemUiProxy.INSTANCE.get(context));
IconProvider iconProvider = new IconProvider(context);
//app的图标
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
mIconCache.registerTaskVisualsChangeListener(this);
//app的缩略图
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
//这个很重要,监听task改变的回调设置
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
}
10.recent启动的流程
在03NavigationBar的4.7小节的recents点击事件里有讲到 最终的事件处理是交给一个服务了,再简单贴下相关的代码
OverviewProxyService.java
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
private void internalConnectToCurrentUser() {
//...
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) {
//...
}
相关的服务
10.1.TouchInteractionService.java
<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>
TouchInteractionService里的binder如下
private final TISBinder mTISBinder = new TISBinder();
//TISBinder
public class TISBinder extends IOverviewProxy.Stub {
//...
//这个就是recents按钮点击以后调用的代码
public void onOverviewToggle() {
//...
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
//这里
mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
}
>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());
}
10.2.OverviewCommandHelper.java
private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
BaseActivityInterface<?, T> activityInterface =
mOverviewComponentObserver.getActivityInterface();
//只有overview在前台的话不为null,
RecentsView recents = activityInterface.getVisibleRecentsView();
//这里只分析下点击recents按钮的逻辑,其他type不研究
if (recents == null) {
//..
} else {
switch (cmd.type) {
//..
case TYPE_TOGGLE://recents已经显示的情况,再点击就会执行这个
return launchTask(recents, getNextTask(recents), cmd);
case TYPE_HOME:
recents.startHome();
return true;
}
}
//第一种情况,launcher在前台,这里基本会返回true
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);
}
//..
//最终一般会走else这里,启动intent
} else {
Intent intent = new Intent(interactionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
gestureState, intent, interactionHandler);
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
cmd.mActiveCallbacks.addListener(recentAnimListener);
}
>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;
}
>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;
}
}
#getNextTask
看下上边的taskView的获取逻辑,先查找有没有runningTaskView,没有的话直接返回第一个,有的话,taskid加1查找nextTaskView,有的话返回,没有的话就还用runningtaskView
private TaskView getNextTask(RecentsView view) {
final TaskView runningTaskView = view.getRunningTaskView();
if (runningTaskView == null) {
return view.getTaskViewAt(0);
} else {
final TaskView nextTask = view.getNextTaskView();
return nextTask != null ? nextTask : runningTaskView;
}
}
>情况一launcher在前台
测试步骤: 点击home键回到桌面,然后点击recents按钮
launcher在前台的逻辑 LauncherActivityInterface.java
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
Launcher launcher = getVisibleLauncher();
if (launcher == null) {
return false;//在前台的话,这里不会为null的,所以会走到下边
}
closeOverlay();
//会走这里
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(),
onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
return true;
}
goToState里会会调listener里的方法,我们的RecentsView里就有添加listener,所以会做出对应的操作,显示或者隐藏自己。
>情况二launcher在后台?
测试步骤:随便打开一个app,然后点击recents按钮。
启动的intent内容
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) }
代码的话是这个
private static Intent createHomeIntent() {
return new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
对应的是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>
看完intent的内容,想当然的以为这个就是跳转到QuickstepLauncher,结果我在onResume里添加日志啥都没打印,可recentView页面确实出来了,这个页面也是launcher页面的view。后边一路顺着和intent有关的代码看了下,啥也看不出来,先简单记录下相关的.
后边有看到SurfaceController,感觉是把launcher绘制出来了,顶层那个activity没有绘制或者绘制到底层去了,到底咋弄的期待高手了。
#1 else
// ----------#2
cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
gestureState, intent, interactionHandler);
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
//总共加了4个listener,随便注释一个,overview页面都出不来,里边的回调太复杂了,啥也看不懂。
cmd.mActiveCallbacks.addListener(recentAnimListener);
#2 startRecentsAnimation
TaskAnimationManager.java
final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
mLastGestureState = gestureState;
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 {
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, eventTime, mCallbacks, null, null));
}
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
return mCallbacks;
}
#3 RecentsAnimationCallbacks
4里的回调会调用这个方法,这个方法里可以看到,最终主要是调用listener的方法,我们在#1#2里添加了4个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);
}
});
}
}
#4 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);
//这里的handler就是#2里的mCallbacks,会调用mCallbacks添加的listener
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);
}
};
}
getService().startRecentsActivity(intent, eventTime, runner);
return true;
} catch (Exception e) {
return false;
}
}
#5 getService()
ActivityTaskManager.java
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
最终实现类是下边这个类
#6 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);
// Start a new recents animation
final RecentsAnimation anim = new RecentsAnimation(this, mTaskSupervisor,
getActivityStartController(), mWindowManager, intent, recentsComponent,
recentsFeatureId, recentsUid, caller);
if (recentsAnimationRunner == null) {
} else {//-----------走的else,#7
anim.startRecentsActivity(recentsAnimationRunner, eventTime);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
#7 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);
// ---------#8
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();//恢复窗口布局
}
}
#8 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;
}
// --------------------#9
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 */);
}
}
#9 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);
//----- #11
task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden,
ANIMATION_TYPE_RECENTS, finishedCallback);
task.commitPendingTransaction();
mPendingAnimations.add(taskAdapter);
return taskAdapter;
}
#10 task type
记录下常见的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);
}
#11 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类,这个看起来像是我们要的东西,不细究了,太复杂了。
10.3.StateManager.java
>addStateListener
LauncherRecentsView里用到这个,监听状态的改变好更改自己的可见性。
public void addStateListener(StateListener listener) {
mListeners.add(listener);
}
>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);
}
}
11. 总结
- 主要介绍了,我们点击recents按钮以launcher3里边服务如何启动对应的页面
- recent view的布局结构
- 自定义的RecentsView逻辑,监听launcher的状态变化,隐藏显示自己
- 子控件TaskView