android framework13-launcher3【02recents】

2,731 阅读25分钟

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>

image.png 上图的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内容的时候,显示的空白提示,一个图片,一段文字,如下 image.png

    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:
            //补充2updateIsBeingDraggedOnTouchDown(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 {

image.png

>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.手机模式

>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用的