• Android 7.1 SystemUI--Multi-Window多窗口模式


    PhoneStatusBar.java

        private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {
    
            @Override
            public boolean onLongClick(View v) {
                if (mRecents == null || !ActivityManager.supportsMultiWindow()
                        || !getComponent(Divider.class).getView().getSnapAlgorithm()
                                .isSplitScreenFeasible()) {
                    return false;
                }
    
                toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
                        MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
                return true;
            }
        };
        @Override
        protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
            if (mRecents == null) {
                return;
            }
            int dockSide = WindowManagerProxy.getInstance().getDockSide();
            if (dockSide == WindowManager.DOCKED_INVALID) {
                // 进入分屏
                mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
                        ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
            } else {
                // 退出分屏
                EventBus.getDefault().send(new UndockingTaskEvent());
                if (metricsUndockAction != -1) {
                    MetricsLogger.action(mContext, metricsUndockAction);
                }
            }
        }

    Recents.java

    @Override
        public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
                int metricsDockAction) {
            // Ensure the device has been provisioned before allowing the user to interact with
            // recents
            if (!isUserSetup()) {
                return false;
            }
    
            Point realSize = new Point();
            if (initialBounds == null) {
                mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
                        .getRealSize(realSize);
                initialBounds = new Rect(0, 0, realSize.x, realSize.y);
            }
    
            int currentUser = sSystemServicesProxy.getCurrentUser();
            SystemServicesProxy ssp = Recents.getSystemServices();
            ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
            boolean screenPinningActive = ssp.isScreenPinningActive();
            boolean isRunningTaskInHomeStack = runningTask != null &&
                    SystemServicesProxy.isHomeStack(runningTask.stackId);
            if (runningTask != null && !isRunningTaskInHomeStack && !screenPinningActive) {
                logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
    // 可分屏
    if (runningTask.isDockable) { if (metricsDockAction != -1) { MetricsLogger.action(mContext, metricsDockAction, runningTask.topActivity.flattenToShortString()); } if (sSystemServicesProxy.isSystemUser(currentUser)) { mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds); } else { if (mSystemToUserCallbacks != null) { IRecentsNonSystemUserCallbacks callbacks = mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); if (callbacks != null) { try { callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds); } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); } } else { Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); } } } mDraggingInRecentsCurrentUser = currentUser; return true; } else {
    // 不支持分屏 EventBus.getDefault().send(
    new ShowUserToastEvent( R.string.recents_incompatible_app_message, Toast.LENGTH_SHORT)); return false; } } else { return false; } }

     RecentsImpl.java

    public void dockTopTask(int topTaskId, int dragMode,
                int stackCreateMode, Rect initialBounds) {
            SystemServicesProxy ssp = Recents.getSystemServices();
    
            // Make sure we inform DividerView before we actually start the activity so we can change
            // the resize mode already.
            if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
                EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
                showRecents(
                        false /* triggeredFromAltTab */,
                        dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
                        false /* animate */,
                        true /* launchedWhileDockingTask*/,
                        false /* fromHome */,
                        DividerView.INVALID_RECENTS_GROW_TARGET);
            }
        }

    SystemServicesProxy.java

        /** Docks an already resumed task to the side of the screen. */
        public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
            if (mIam == null) {
                return false;
            }
    
            try {
                return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
                        false /* animate */, initialBounds, true /* moveHomeStackFront */ );
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            return false;
        }

    mIam 是 IActivityManager 对象

    ActivityManagerService.java

     /**
         * Moves the input task to the docked stack.
         *
         * @param taskId Id of task to move.
         * @param createMode The mode the docked stack should be created in if it doesn't exist
         *                   already. See
         *                   {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT}
         *                   and
         *                   {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
         * @param toTop If the task and stack should be moved to the top.
         * @param animate Whether we should play an animation for the moving the task
         * @param initialBounds If the docked stack gets created, it will use these bounds for the
         *                      docked stack. Pass {@code null} to use default bounds.
         */
        @Override
        public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
                Rect initialBounds, boolean moveHomeStackFront) {
            enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
            synchronized (this) {
                long ident = Binder.clearCallingIdentity();
                try {
                    if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
                            + " to createMode=" + createMode + " toTop=" + toTop);
                    mWindowManager.setDockedStackCreateState(createMode, initialBounds);
                    final boolean moved = mStackSupervisor.moveTaskToStackLocked(
                            taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
                            animate, DEFER_RESUME);
                    if (moved) {
                        if (moveHomeStackFront) {
                            mStackSupervisor.moveHomeStackToFront("moveTaskToDockedStack");
                        }
                        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                    }
                    return moved;
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
        }

    ActivityStackSupervisor.java

    boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
                String reason, boolean animate, boolean deferResume) {
            final TaskRecord task = anyTaskForIdLocked(taskId);
            if (task == null) {
                Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
                return false;
            }
    
            if (task.stack != null && task.stack.mStackId == stackId) {
                // You are already in the right stack silly...
                Slog.i(TAG, "moveTaskToStack: taskId=" + taskId + " already in stackId=" + stackId);
                return true;
            }
    
            if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
                throw new IllegalArgumentException("moveTaskToStack:"
                        + "Attempt to move task " + taskId + " to unsupported freeform stack");
            }
    
            final ActivityRecord topActivity = task.getTopActivity();
            final int sourceStackId = task.stack != null ? task.stack.mStackId : INVALID_STACK_ID;
            final boolean mightReplaceWindow =
                    StackId.replaceWindowsOnTaskMove(sourceStackId, stackId) && topActivity != null;
            if (mightReplaceWindow) {
                // We are about to relaunch the activity because its configuration changed due to
                // being maximized, i.e. size change. The activity will first remove the old window
                // and then add a new one. This call will tell window manager about this, so it can
                // preserve the old window until the new one is drawn. This prevents having a gap
                // between the removal and addition, in which no window is visible. We also want the
                // entrance of the new window to be properly animated.
                // Note here we always set the replacing window first, as the flags might be needed
                // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
                mWindowManager.setReplacingWindow(topActivity.appToken, animate);
            }
    
            mWindowManager.deferSurfaceLayout();
            final int preferredLaunchStackId = stackId;
            boolean kept = true;
            try {
                final ActivityStack stack = moveTaskToStackUncheckedLocked(
                        task, stackId, toTop, forceFocus, reason + " moveTaskToStack");
                stackId = stack.mStackId;
    
                if (!animate) {
                    stack.mNoAnimActivities.add(topActivity);
                }
    
                // We might trigger a configuration change. Save the current task bounds for freezing.
                mWindowManager.prepareFreezingTaskBounds(stack.mStackId);
    
                // Make sure the task has the appropriate bounds/size for the stack it is in.
                if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
                    kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
                            !mightReplaceWindow, deferResume);
                } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
                    Rect bounds = task.getLaunchBounds();
                    if (bounds == null) {
                        stack.layoutTaskInStack(task, null);
                        bounds = task.mBounds;
                    }
                    kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow,
                            deferResume);
                } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
                    kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
                            !mightReplaceWindow, deferResume);
                }
            } finally {
                mWindowManager.continueSurfaceLayout();
            }
    
            if (mightReplaceWindow) {
                // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
                // window), we need to clear the replace window settings. Otherwise, we schedule a
                // timeout to remove the old window if the replacing window is not coming in time.
                mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
            }
    
            if (!deferResume) {
    
                // The task might have already been running and its visibility needs to be synchronized with
                // the visibility of the stack / windows.
                ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
                resumeFocusedStackTopActivityLocked();
            }
    
            handleNonResizableTaskIfNeeded(task, preferredLaunchStackId, stackId);
    
            return (preferredLaunchStackId == stackId);
        }

                final ActivityStack stack = moveTaskToStackUncheckedLocked(
                        task, stackId, toTop, forceFocus, reason + " moveTaskToStack");

    /**
         * Moves the specified task record to the input stack id.
         * WARNING: This method performs an unchecked/raw move of the task and
         * can leave the system in an unstable state if used incorrectly.
         * Use {@link #moveTaskToStackLocked} to perform safe task movement to a stack.
         * @param task Task to move.
         * @param stackId Id of stack to move task to.
         * @param toTop True if the task should be placed at the top of the stack.
         * @param forceFocus if focus should be moved to the new stack
         * @param reason Reason the task is been moved.
         * @return The stack the task was moved to.
         */
        ActivityStack moveTaskToStackUncheckedLocked(
                TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
    
            if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
                throw new IllegalStateException("moveTaskToStackUncheckedLocked: Device doesn't "
                        + "support multi-window task=" + task + " to stackId=" + stackId);
            }
    
            final ActivityRecord r = task.topRunningActivityLocked();
            final ActivityStack prevStack = task.stack;
            final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
            final boolean wasResumed = prevStack.mResumedActivity == r;
            // In some cases the focused stack isn't the front stack. E.g. pinned stack.
            // Whenever we are moving the top activity from the front stack we want to make sure to move
            // the stack to the front.
            final boolean wasFront = isFrontStack(prevStack)
                    && (prevStack.topRunningActivityLocked() == r);
    
            if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
                // We don't allow moving a unresizeable task to the docked stack since the docked
                // stack is used for split-screen mode and will cause things like the docked divider to
                // show up. We instead leave the task in its current stack or move it to the fullscreen
                // stack if it isn't currently in a stack.
                stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
                Slog.w(TAG, "Can not move unresizeable task=" + task
                        + " to docked stack. Moving to stackId=" + stackId + " instead.");
            }
            if (stackId == FREEFORM_WORKSPACE_STACK_ID
                    && mService.mUserController.shouldConfirmCredentials(task.userId)) {
                stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
                Slog.w(TAG, "Can not move locked profile task=" + task
                        + " to freeform stack. Moving to stackId=" + stackId + " instead.");
            }
    
            // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
            // if a docked stack is created below which will lead to the stack we are moving from and
            // its resizeable tasks being resized.
            task.mTemporarilyUnresizable = true;
            final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
            task.mTemporarilyUnresizable = false;
            mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
            stack.addTask(task, toTop, reason);
    
            // If the task had focus before (or we're requested to move focus),
            // move focus to the new stack by moving the stack to the front.
            stack.moveToFrontAndResumeStateIfNeeded(
                    r, forceFocus || wasFocused || wasFront, wasResumed, reason);
    
            return stack;
        }

    WindowManagerService.java

        public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
            synchronized (mWindowMap) {
                if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: moving taskId=" + taskId
                        + " to stackId=" + stackId + " at " + (toTop ? "top" : "bottom"));
                Task task = mTaskIdToTask.get(taskId);
                if (task == null) {
                    if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: could not find taskId=" + taskId);
                    return;
                }
                TaskStack stack = mStackIdToStack.get(stackId);
                if (stack == null) {
                    if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: could not find stackId=" + stackId);
                    return;
                }
                task.moveTaskToStack(stack, toTop);
                final DisplayContent displayContent = stack.getDisplayContent();
                displayContent.layoutNeeded = true;
                mWindowPlacerLocked.performSurfacePlacement();
            }
        }

    WindowSurfacePlacer.java

    /**
     * Positions windows and their surfaces.
     *
     * It sets positions of windows by calculating their frames and then applies this by positioning
     * surfaces according to these frames. Z layer is still assigned withing WindowManagerService.
     */
    class WindowSurfacePlacer {

     final void performSurfacePlacement() {
            if (mDeferDepth > 0) {
                return;
            }
            int loopCount = 6;
            do {
                mTraversalScheduled = false;
                performSurfacePlacementLoop();
                mService.mH.removeMessages(DO_TRAVERSAL);
                loopCount--;
            } while (mTraversalScheduled && loopCount > 0);
            mWallpaperActionPending = false;
        }

    }

  • 相关阅读:
    MySQL复制延时排查
    SQL优化之【类型转换】
    Twemproxy 介绍与使用
    Redis Cluster 3.0搭建与使用
    unauthenticated user reading from net
    XtraBackup之踩过的坑
    Redis学习之实现优先级消息队列
    如何保证接口的幂等性
    Redis缓存网页及数据行
    Rabbitmq 消费者的推模式与拉模式(go语言版本)
  • 原文地址:https://www.cnblogs.com/onelikeone/p/7478762.html
Copyright © 2020-2023  润新知