關(guān)注個人簡介,技術(shù)不迷路~
1.簡介
這里主要介紹下點(diǎn)擊導(dǎo)航欄的recents按鈕瓢对,跳轉(zhuǎn)的頁面。我以前以為這東西是個單獨(dú)的app胰苏,看了代碼才發(fā)現(xiàn)硕蛹,它是launcher的一部分。我們打開源碼/packages/apps/Launcher3 目錄下硕并,發(fā)現(xiàn)除了res法焰,src,清單文件外倔毙,還有2個目錄埃仪,go目錄是給低配置的機(jī)器用的簡化版的代碼,quickstep是我們要看的東西普监。
2.Android.bp
打開Launcher3目錄下的這個文件贵试,搜索下android_app,可以發(fā)現(xiàn)有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】毙玻,官方源碼默認(rèn)的是這個,額,我這編譯的是phone用的廊散,可以看到下邊用的Launcher3QuickStep
# /system_ext packages
PRODUCT_PACKAGES += \
Launcher3QuickStep \
Provision \
Settings \
StorageManager \
SystemUI \
WallpaperCropper \
3.layout
底層布局還是上一篇里用到launcher.xml,里邊有個這樣的include桑滩,在默認(rèn)的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
顧名思義允睹,就是桌面的狀態(tài)运准,有10種,這里就注釋下我了解的幾種
private static final LauncherState[] sAllStates = new LauncherState[10];
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
case NORMAL_STATE_ORDINAL:
return "Normal";//點(diǎn)擊home鍵回到的頁面就是缭受,默認(rèn)啟動就是這個
case SPRING_LOADED_STATE_ORDINAL:
return "SpringLoaded";//拖動圖標(biāo)的時候胁澳,頂部功能按鈕(removed)出現(xiàn)的時候
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圖標(biāo)的那個
case BACKGROUND_APP_STATE_ORDINAL:
//launcher在后臺的時候,點(diǎn)擊recents米者,先變成這個狀態(tài)韭畸,再變成overview轉(zhuǎn)改
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
點(diǎn)擊recents按鈕顯示的頁面就是這個容器了,這里通過在構(gòu)造方法里添加監(jiān)聽的方法蔓搞,獲取state狀態(tài)胰丁,同步修改自己的可見性。
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改變的監(jiān)聽
mActivity.getStateManager().addStateListener(this);
}
public void startHome() {
//home頁就是normal狀態(tài)
mActivity.getStateManager().goToState(NORMAL);
AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
//這個就是回調(diào)喂分,狀態(tài)改變開始
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);
}
//這個就是回調(diào)锦庸,狀態(tài)改變結(jié)束
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
reset();
}
//點(diǎn)擊recent的話狀態(tài)就是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);
//..數(shù)據(jù)的獲取都在這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相關(guān)的圖標(biāo)和文字
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里會調(diào)用
protected void setupViews() {
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
RecentsView overviewPanel = getOverviewPanel();
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController(), getStatsLogManager());
//這調(diào)用
overviewPanel.init(mActionsView, mSplitSelectStateController);
//..
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
6.2.onAttachedToWindow
主要是添加一堆監(jiān)聽
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
重新設(shè)置下taskView的屬性酿愧,在重新add child以后會執(zhí)行
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
設(shè)置自己的可見性,并同步修改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);
}
}
}
>調(diào)用的地方
-
設(shè)置透明度邀泉,為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汇恤,也就是默認(rèn)啟動的時候是不可見的
public void init(OverviewActionsView actionsView,
SplitSelectStateController splitPlaceholderView) {
super.init(actionsView, splitPlaceholderView);
setContentAlpha(0);
}
上邊的方法是在QuickstepLauncher.java的setupViews的方法里調(diào)用的
overviewPanel.init(mActionsView, mSplitSelectStateController);
-
下邊這個方法是動畫開始的時候會凍結(jié)view的可見性,結(jié)束的時候會恢復(fù)為false
public void setFreezeViewVisibility(boolean freezeViewVisibility) { if (mFreezeViewVisibility != freezeViewVisibility) { mFreezeViewVisibility = freezeViewVisibility; if (!mFreezeViewVisibility) { setVisibility(mContentAlpha > 0 ? VISIBLE : INVISIBLE); } } }
>透明度的改變
前邊有講過拔恰,launcher的view顯示有10種狀態(tài)因谎,我們也簡單分析了幾種常見的。overview的可見性也是通過改變state來實(shí)現(xiàn)的颜懊。有專門的StateManager類來修改state财岔,還有state的處理類。這里簡單貼下非動畫的改變狀態(tài)的情況河爹,動畫的麻煩匠璧,還有l(wèi)istener,就不看了咸这。
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狀態(tài),透明度為1可見媳维,非overview狀態(tài)酿雪,透明度為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方法侄刽,可以看到指黎,就是設(shè)置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.數(shù)據(jù)的獲取
state改變的回調(diào)哪里調(diào)用的后邊再分析。
>onStateTransitionStart
public void onStateTransitionStart(LauncherState toState) {
//overview狀態(tài)的話這個為true
setOverviewStateEnabled(toState.overviewUi);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
}
setFreezeViewVisibility(true);
}
>setOverviewStateEnabled
overview狀態(tài)的話這個為true当叭,非overview的時候又成了false
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
//更新數(shù)據(jù)
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默認(rèn)是false的茬故,當(dāng)我們點(diǎn)擊recent以后就要顯示overview了,這時候handleTaskStackChanges為true蚁鳖,后邊幾個條件都是true的磺芭。當(dāng)退出overview的時候這里的值又成了false,下次再次進(jìn)入overview的時候又成了true
private void updateTaskStackListenerState() {
boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow()
&& getWindowVisibility() == VISIBLE;
if (handleTaskStackChanges != mHandleTaskStackChanges) {
mHandleTaskStackChanges = handleTaskStackChanges;
if (handleTaskStackChanges) {
reloadIfNeeded();
}
}
}
//reloadIfNeeded調(diào)用的是TaskList的方法獲取數(shù)據(jù)
public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
}
>reloadIfNeeded
這個方法在每次overview顯示的時候都會調(diào)用醉箕,
public void reloadIfNeeded() {
if (!mModel.isTaskListValid(mTaskListChangeId)) {
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
.getFilter(mFilterState.getPackageNameToFilter()));
}
}
#onStateTransitionComplete
可以看到钾腺,每次退出overview回到normal狀態(tài)的時候徙垫,會調(diào)用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
這個就是獲取數(shù)據(jù)以后的回調(diào)了,主要處理child的添加
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
if (mPendingAnimation != null) {
mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
return;
}
mLoadPlanEverApplied = true;
//數(shù)據(jù)為null的情況
if (taskGroups == null || taskGroups.isEmpty()) {
//移除所有相關(guān)的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;
}
//..中間一堆代碼就是獲取容器舊的狀態(tài)放棒,方便后邊恢復(fù)姻报,因?yàn)橄逻厱瞥械腸hild
removeAllViews();
//..
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
//根據(jù)數(shù)據(jù),循環(huán)添加child间螟,主要有兩種吴旋,single,group厢破,其實(shí)還有個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);
//下邊就是綁定數(shù)據(jù)了
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 屬性設(shè)置
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
這個就是綁定數(shù)據(jù)了荣瑟,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列表里摩泪,點(diǎn)擊某個app上邊的圖標(biāo)笆焰,有個彈框,選擇split top见坑,完事這個app就顯示在頂部了嚷掠,這時候再選一個要同時顯示的app,在其縮略圖上點(diǎn)一下荞驴,就能看到這種效果了不皆。
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的圖標(biāo)
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
mIconCache.registerTaskVisualsChangeListener(this);
//app的縮略圖
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
//這個很重要粟焊,監(jiān)聽task改變的回調(diào)設(shè)置
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
}
10.recent啟動的流程
在03NavigationBar的4.7小節(jié)的recents點(diǎn)擊事件里有講到 最終的事件處理是交給一個服務(wù)了,再簡單貼下相關(guān)的代碼
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) {
//...
}
相關(guān)的服務(wù)
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按鈕點(diǎn)擊以后調(diào)用的代碼
public void onOverviewToggle() {
//...
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
//這里
mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
}
>onUserUnlocked
解鎖屏幕以后的回調(diào)
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();
//這里只分析下點(diǎn)擊recents按鈕的邏輯项棠,其他type不研究
if (recents == null) {
//..
} else {
switch (cmd.type) {
//..
case TYPE_TOGGLE://recents已經(jīng)顯示的情況,再點(diǎn)擊就會執(zhí)行這個
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結(jié)果是false透典,會繼續(xù)往下走
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狀態(tài)的話返回null峭咒,overview狀態(tài)就返回對應(yīng)的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顯示的時候,再次點(diǎn)擊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在前臺
測試步驟: 點(diǎn)擊home鍵回到桌面霍狰,然后點(diǎn)擊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里會會調(diào)listener里的方法蔗坯,我們的RecentsView里就有添加listener渠牲,所以會做出對應(yīng)的操作,顯示或者隱藏自己步悠。
>情況二launcher在后臺?
測試步驟:隨便打開一個app瘫镇,然后點(diǎn)擊recents按鈕鼎兽。
啟動的intent內(nèi)容
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);
}
對應(yīng)的是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的內(nèi)容,想當(dāng)然的以為這個就是跳轉(zhuǎn)到QuickstepLauncher铣除,結(jié)果我在onResume里添加日志啥都沒打印谚咬,可recentView頁面確實(shí)出來了,這個頁面也是launcher頁面的view尚粘。后邊一路順著和intent有關(guān)的代碼看了下择卦,啥也看不出來,先簡單記錄下相關(guān)的.
后邊有看到SurfaceController郎嫁,感覺是把launcher繪制出來了秉继,頂層那個activity沒有繪制或者繪制到底層去了,到底咋弄的期待高手了泽铛。
#1 else
// ----------#2
cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
gestureState, intent, interactionHandler);
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
//總共加了4個listener尚辑,隨便注釋一個,overview頁面都出不來盔腔,里邊的回調(diào)太復(fù)雜了杠茬,啥也看不懂。
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里的回調(diào)會調(diào)用這個方法弛随,這個方法里可以看到瓢喉,最終主要是調(diào)用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栓票,會調(diào)用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);
}
};
最終實(shí)現(xiàn)類是下邊這個類
#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();//恢復(fù)窗口布局
}
}
#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類盐杂,這個看起來像是我們要的東西逗载,不細(xì)究了哆窿,太復(fù)雜了。
10.3.StateManager.java
>addStateListener
LauncherRecentsView里用到這個厉斟,監(jiān)聽狀態(tài)的改變好更改自己的可見性挚躯。
public void addStateListener(StateListener listener) {
mListeners.add(listener);
}
>goToState
private void goToState(
STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
animated &= areAnimatorsEnabled();
//要改變的狀態(tài)和activity當(dāng)前狀態(tài)一樣,直接回調(diào)即可
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();
//回調(diào)listener
onStateTransitionStart(state);
//有handler的話調(diào)用下對應(yīng)的方法
for (StateHandler handler : getStateHandlers()) {
handler.setState(state);
}
//回調(diào)listener
onStateTransitionEnd(state);
// Run any queued runnable
if (listener != null) {
listener.onAnimationEnd(null);
}
return;
}
if (delay > 0) {
//延遲后繼續(xù)調(diào)用下邊的方法
} else {
//帶動畫過程的擦秽,不看了
goToStateAnimated(state, fromState, listener);
}
}
11. 總結(jié)
- 主要介紹了码荔,我們點(diǎn)擊recents按鈕以launcher3里邊服務(wù)如何啟動對應(yīng)的頁面
- recent view的布局結(jié)構(gòu)
- 自定義的RecentsView邏輯,監(jiān)聽launcher的狀態(tài)變化感挥,隱藏顯示自己
- 子控件TaskView