InputMethod窗口、wallpaper窗口管理关键函数分析

孟福
2023-12-01

1、findDesiredInputMethodWindowIndexLocked()

从名字可以出来,这个函数是寻找InputMethodWindow要放置的位置,分析这个函数后自然就知道对于输入法窗口的排列规则。

    int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
        // TODO(multidisplay): Needs some serious rethought when the target and IME are not on the
        // same display. Or even when the current IME/target are not on the same screen as the next
        // IME/target. For now only look for input windows on the main screen.
        WindowList windows = getDefaultWindowListLocked();
        WindowState w = null;
        int i;
        for (i = windows.size() - 1; i >= 0; --i) {      //①从窗口列表的最顶端开始遍历窗口列表,如果该窗口可以成为IME Target,那么该循环退出,即遍历出第一个输入法目标窗口保存在W中,w也可能是mWindows[0];什么情况下窗口才可以成为IME的Target呢?情况一:窗口属性都没有设置FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM属性;情况二:窗口属性同时设置了FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM属性;情况三:TYPE_APPLICATION_STARTING类型窗口也可以作为IME的Target;
            WindowState win = windows.get(i);

            if (DEBUG_INPUT_METHOD && willMove) Slog.i(TAG, "Checking window @" + i
                    + " " + win + " fl=0x" + Integer.toHexString(win.mAttrs.flags));
            if (canBeImeTarget(win)) {               //②如果遍历到一个窗口可以作为IME 的Target,正常情况下那么该Target之上的窗口位置便是输入法位置,特殊情况便是下面那些矫正逻辑,不必太过关心;
                w = win;
                //Slog.i(TAG, "Putting input method here!");

                // Yet more tricksyness!  If this window is a "starting"
                // window, we do actually want to be on top of it, but
                // it is not -really- where input will go.  So if the caller
                // is not actually looking to move the IME, look down below
                // for a real window to target...
                if (!willMove
                        && w.mAttrs.type == TYPE_APPLICATION_STARTING
                        && i > 0) {
                    WindowState wb = windows.get(i-1);
                    if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) {
                        i--;
                        w = wb;
                    }
                }
                break;
            }
        }

        // Now w is either mWindows[0] or an IME (or null if mWindows is empty).           

        if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG, "Proposed new IME target: " + w);

        // Now, a special case -- if the last target's window is in the
        // process of exiting, and is above the new target, keep on the
        // last target to avoid flicker.  Consider for example a Dialog with
        // the IME shown: when the Dialog is dismissed, we want to keep
        // the IME above it until it is completely gone so it doesn't drop
        // behind the dialog or its full-screen scrim.
        final WindowState curTarget = mInputMethodTarget;
        if (curTarget != null
                && curTarget.isDisplayedLw()
                && curTarget.isClosing()
                && (w == null || curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) {
            if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, not changing");
            return windows.indexOf(curTarget) + 1;
        }

        if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Desired input method target="
                + w + " willMove=" + willMove);

        if (willMove && w != null) {     
            AppWindowToken token = curTarget == null ? null : curTarget.mAppToken;
            if (token != null) {

                // Now some fun for dealing with window animations that
                // modify the Z order.  We need to look at all windows below
                // the current target that are in this app, finding the highest
                // visible one in layering.
                WindowState highestTarget = null;
                int highestPos = 0;
                if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
                    WindowList curWindows = curTarget.getWindowList();
                    int pos = curWindows.indexOf(curTarget);
                    while (pos >= 0) {
                        WindowState win = curWindows.get(pos);
                        if (win.mAppToken != token) {
                            break;
                        }
                        if (!win.mRemoved) {
                            if (highestTarget == null || win.mWinAnimator.mAnimLayer >
                                    highestTarget.mWinAnimator.mAnimLayer) {
                                highestTarget = win;
                                highestPos = pos;
                            }
                        }
                        pos--;
                    }
                }

                if (highestTarget != null) {
                    if (DEBUG_INPUT_METHOD) Slog.v(TAG, mAppTransition + " " + highestTarget
                            + " animating=" + highestTarget.mWinAnimator.isAnimating()
                            + " layer=" + highestTarget.mWinAnimator.mAnimLayer
                            + " new layer=" + w.mWinAnimator.mAnimLayer);

                    if (mAppTransition.isTransitionSet()) {
                        // If we are currently setting up for an animation,
                        // hold everything until we can find out what will happen.
                        mInputMethodTargetWaitingAnim = true;
                        mInputMethodTarget = highestTarget;
                        return highestPos + 1;
                    } else if (highestTarget.mWinAnimator.isAnimating() &&
                            highestTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer) {
                        // If the window we are currently targeting is involved
                        // with an animation, and it is on top of the next target
                        // we will be over, then hold off on moving until
                        // that is done.
                        mInputMethodTargetWaitingAnim = true;
                        mInputMethodTarget = highestTarget;
                        return highestPos + 1;
                    }
                }
            }
        }

        //Slog.i(TAG, "Placing input method @" + (i+1));
        if (w != null) {
            if (willMove) {
                if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to "
                        + w + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4)));
                mInputMethodTarget = w;
                mInputMethodTargetWaitingAnim = false;
                if (w.mAppToken != null) {
                    setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment);
                } else {
                    setInputMethodAnimLayerAdjustment(0);
                }
            }
            return i+1;
        }
        if (willMove) {
            if (DEBUG_INPUT_METHOD) Slog.w(TAG, "Moving IM target from " + curTarget + " to null."
                    + (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4)));
            mInputMethodTarget = null;
            setInputMethodAnimLayerAdjustment(0);
        }
        return -1;
    }

总结:只有参数willMove为true时mInputMethodTarget才有可能更改。正常情况下一般找到IME Target便可以确定输入法窗口位置便是Target之上的位置,特殊情况就不用仔细研究了,如果找不到IME Target便return -1。只要知道该函数return一个输入法位置,并且找到mInputMethodTarget窗口(输入法目标窗口)。把这个函数简化下就是从窗口列表中找到最顶位置并且canBeImeTarget()的窗口作为输入法目标窗口,输入法窗口位置等于输入法目标窗口位置+1.

2、moveInputMethodWindowsIfNeededLocked()

    boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) {
        final WindowState imWin = mInputMethodWindow;
        final int DN = mInputMethodDialogs.size();
        if (imWin == null && DN == 0) {
            return false;
        }

        // TODO(multidisplay): IMEs are only supported on the default display.
        WindowList windows = getDefaultWindowListLocked();

        int imPos = findDesiredInputMethodWindowIndexLocked(true);   //①寻找输入法窗口位置;
        if (imPos >= 0) {                                     //②如果找到输入法窗口要放置的位置;
            // In this case, the input method windows are to be placed
            // immediately above the window they are targeting.

            // First check to see if the input method windows are already
            // located here, and contiguous.
            final int N = windows.size();
            WindowState firstImWin = imPos < N
                    ? windows.get(imPos) : null;

            // Figure out the actual input method window that should be
            // at the bottom of their stack.
            WindowState baseImWin = imWin != null
                    ? imWin : mInputMethodDialogs.get(0);
            if (baseImWin.mChildWindows.size() > 0) {
                WindowState cw = baseImWin.mChildWindows.get(0);
                if (cw.mSubLayer < 0) baseImWin = cw;
            }

            if (firstImWin == baseImWin) {            //③这个if逻辑就是判断输入法窗口是否处在①中找到的位置上 ,并且输入法窗口及输入法对话窗口是否连续,如果位置正确并且连续,自然无需重新移动输入法窗口;
                // The windows haven't moved...  but are they still contiguous?
                // First find the top IM window.
                int pos = imPos+1;
                while (pos < N) {
                    if (!(windows.get(pos)).mIsImWindow) {
                        break;
                    }
                    pos++;
                }
                pos++;
                // Now there should be no more input method windows above.
                while (pos < N) {
                    if ((windows.get(pos)).mIsImWindow) {
                        break;
                    }
                    pos++;
                }
                if (pos >= N) {              //④如果这个条件满足,自然是输入法窗口位置正确,并且所有输入法窗口连续;
                    // Z order is good.
                    // The IM target window may be changed, so update the mTargetAppToken.
                    if (imWin != null) {
                        imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
                    }
                    return false;
                }
            }

            if (imWin != null) {         //⑤如果④中条件无法满足,那么自然需要重新排列输入法窗口位置;
                if (DEBUG_INPUT_METHOD) {
                    Slog.v(TAG, "Moving IM from " + imPos);
                    logWindowList(windows, "  ");
                }
                imPos = tmpRemoveWindowLocked(imPos, imWin);     //⑥移除输入法窗口,imPos作为参数穿进去,如果移除的窗口位于imPos之下,那么imPos-=移除窗口数;如果移除的窗口在imPos之上,那么imPos值不变;imPos值后续会用来作为插入输入法窗口位置,Google写代码真牛啊!
                if (DEBUG_INPUT_METHOD) {
                    Slog.v(TAG, "List after removing with new pos " + imPos + ":");
                    logWindowList(windows, "  ");
                }
                imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
                reAddWindowLocked(imPos, imWin);     //⑦重新add输入法窗口到正确的位置上去,下面如果还有输入法对话框窗口,那么add到输入法窗口上一个位置,紧邻;
                if (DEBUG_INPUT_METHOD) {
                    Slog.v(TAG, "List after moving IM to " + imPos + ":");
                    logWindowList(windows, "  ");
                }
                if (DN > 0) moveInputMethodDialogsLocked(imPos+1);
            } else {
                moveInputMethodDialogsLocked(imPos);
            }

        } else {                 //如果输入法窗口位置未找到,那么对于输入法窗口来说,imWin.mTargetAppToken置空,先调用tmpRemoveWindowLocked()将输入法窗口及子窗口remove,然后再调用reAddWindowToListInOrderLocked()重新add进来;
            // In this case, the input method windows go in a fixed layer,
            // because they aren't currently associated with a focus window.

            if (imWin != null) {
                if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Moving IM from " + imPos);
                tmpRemoveWindowLocked(0, imWin);
                imWin.mTargetAppToken = null;
                reAddWindowToListInOrderLocked(imWin);
                if (DEBUG_INPUT_METHOD) {
                    Slog.v(TAG, "List with no IM target:");
                    logWindowList(windows, "  ");
                }
                if (DN > 0) moveInputMethodDialogsLocked(-1);
            } else {
                moveInputMethodDialogsLocked(-1);
            }

        }

        if (needAssignLayers) {
            assignLayersLocked(windows);
        }

        return true;
    }

总结:该函数首先判断输入法窗口是否在正确的位置上,同时是否连续,这样就无需重排;如果要重排,分两种情况,一种是知道输入法窗口要放置的位置,此时先把输入法窗口先进行移除,然后add到要放置的位置即可,一种情况是找不到输入法窗口放置位置,此时也是先把输入法窗口移除,然后调用reAddWindowToListInOrderLocked()添加到窗口列表中,对于输入法对话框窗口,移动到窗口列表的最底部去;

第二部分、壁纸窗口管理

1、只要mWallpaperTarget不为null,那么壁纸就可见。因为WindowState.mObscured一般为false。

  final boolean isWallpaperVisible(WindowState wallpaperTarget) {
        if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
                + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
                + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
                        ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
                + " upper=" + mUpperWallpaperTarget
                + " lower=" + mLowerWallpaperTarget);
        return (wallpaperTarget != null
                        && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
                                && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
                || mUpperWallpaperTarget != null
                || mLowerWallpaperTarget != null;
    }

2、寻找mWallpaperTarget的逻辑

Android 5.0的壁纸窗口调整逻辑改为:如果“目标窗口壁纸”为keyguard,那么直接插入keyguard窗口之下;否则直接放到窗口堆栈最底下;
    int adjustWallpaperWindowsLocked() {
        mInnerFields.mWallpaperMayChange = false;
        boolean targetChanged = false;

        // TODO(multidisplay): Wallpapers on main screen only.
        final DisplayInfo displayInfo = getDefaultDisplayContentLocked().getDisplayInfo();
        final int dw = displayInfo.logicalWidth;
        final int dh = displayInfo.logicalHeight;

        // First find top-most window that has asked to be on top of the
        // wallpaper; all wallpapers go behind it.
        final WindowList windows = getDefaultWindowListLocked();    //获取窗口堆栈;
        int N = windows.size();
        WindowState w = null;
        WindowState foundW = null;
        int foundI = 0;
        WindowState topCurW = null;
        int topCurI = 0;
        int windowDetachedI = -1;
        int i = N;
        while (i > 0) {            //从窗口堆栈顶端开始查找一个带FLAG_SHOW_WALLPAPER属性的可见的绘制完成的窗口,我们把它称作“壁纸目标窗口”,壁纸窗口将会显示在“壁纸目标窗口”之下;
            i--;
            w = windows.get(i);
            if ((w.mAttrs.type == TYPE_WALLPAPER)) {
                if (topCurW == null) {
                    topCurW = w;
                    topCurI = i;
                }
                continue;
            }
            topCurW = null;
            if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
                // If this window's app token is hidden and not animating,
                // it is of no interest to us.
                if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
                    if (DEBUG_WALLPAPER) Slog.v(TAG,
                            "Skipping hidden and not animating token: " + w);
                    continue;
                }
            }
            if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen="
                    + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState);

            // If the app is executing an animation because the keyguard is going away, keep the
            // wallpaper during the animation so it doesn't flicker out.
            final boolean hasWallpaper = (w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
                    || (w.mAppToken != null
                            && w.mWinAnimator.mKeyguardGoingAwayAnimation);
            if (hasWallpaper && w.isOnScreen()
                    && (mWallpaperTarget == w || w.isDrawFinishedLw())) {    //如果找到了一个“壁纸目标窗口”,那么结束while循环,并记录在局部变量foundW和foundI中,foundI指向目标壁纸窗口在窗口堆栈中的位置。如果while循环结束了还没找到,那么foundW=null,foundI=0;
                if (DEBUG_WALLPAPER) Slog.v(TAG,
                        "Found wallpaper target: #" + i + "=" + w);
                foundW = w;
                foundI = i;
                if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) {
                    // The current wallpaper target is animating, so we'll
                    // look behind it for another possible target and figure
                    // out what is going on below.
                    if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w
                            + ": token animating, looking behind.");
                    continue;
                }
                break;
            } else if (w == mAnimator.mWindowDetachedWallpaper) {
                windowDetachedI = i;
            }
        }

        if (foundW == null && windowDetachedI >= 0) {
            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                    "Found animating detached wallpaper activity: #" + i + "=" + w);
            foundW = w;
            foundI = windowDetachedI;
        }

        if (mWallpaperTarget != foundW
                && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) {    //“壁纸目标窗口”该更新了
            if (DEBUG_WALLPAPER_LIGHT) {
                Slog.v(TAG, "New wallpaper target: " + foundW
                        + " oldTarget: " + mWallpaperTarget);
            }

            mLowerWallpaperTarget = null;
            mUpperWallpaperTarget = null;

            WindowState oldW = mWallpaperTarget;
            mWallpaperTarget = foundW;
            targetChanged = true;
            //按理说走到上面这个逻辑就结束,但是有一个特殊case就是oldW和foundW均不为null,且都在做动画,比如keyguard解锁时就是这种情况,oldW指向StatesBar,foundW指向launcher3;

            // Now what is happening...  if the current and new targets are
            // animating, then we are in our super special mode!
            if (foundW != null && oldW != null) {
                boolean oldAnim = oldW.isAnimatingLw();
                boolean foundAnim = foundW.isAnimatingLw();
                if (DEBUG_WALLPAPER_LIGHT) {
                    Slog.v(TAG, "New animation: " + foundAnim
                            + " old animation: " + oldAnim);
                }
                if (foundAnim && oldAnim) {
                    int oldI = windows.indexOf(oldW);
                    if (DEBUG_WALLPAPER_LIGHT) {
                        Slog.v(TAG, "New i: " + foundI + " old i: " + oldI);
                    }
                    if (oldI >= 0) {
                        if (DEBUG_WALLPAPER_LIGHT) {
                            Slog.v(TAG, "Animating wallpapers: old#" + oldI
                                    + "=" + oldW + "; new#" + foundI
                                    + "=" + foundW);
                        }

                        // Set the new target correctly.
                        if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) {
                            if (DEBUG_WALLPAPER_LIGHT) {
                                Slog.v(TAG, "Old wallpaper still the target.");
                            }
                            mWallpaperTarget = oldW;
                            foundW = oldW;
                            foundI = oldI;
                        }
                        // Now set the upper and lower wallpaper targets
                        // correctly, and make sure that we are positioning
                        // the wallpaper below the lower.
                        else if (foundI > oldI) {
                            // The new target is on top of the old one.
                            if (DEBUG_WALLPAPER_LIGHT) {
                                Slog.v(TAG, "Found target above old target.");
                            }
                            mUpperWallpaperTarget = foundW;
                            mLowerWallpaperTarget = oldW;
                            foundW = oldW;
                            foundI = oldI;
                        } else {
                            // The new target is below the old one.
                            if (DEBUG_WALLPAPER_LIGHT) {
                                Slog.v(TAG, "Found target below old target.");
                            }
                            mUpperWallpaperTarget = oldW;
                            mLowerWallpaperTarget = foundW;
                        }
                    }
                }
            }

        } else if (mLowerWallpaperTarget != null) {
            // Is it time to stop animating?
            if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) {
                if (DEBUG_WALLPAPER_LIGHT) {
                    Slog.v(TAG, "No longer animating wallpaper targets!");
                }
                mLowerWallpaperTarget = null;
                mUpperWallpaperTarget = null;
                mWallpaperTarget = foundW;
                targetChanged = true;
            }
        }

        boolean visible = foundW != null;
        if (visible) {                            //如果存在“目标壁纸窗口”,那么visible就为true;
            // The window is visible to the compositor...  but is it visible
            // to the user?  That is what the wallpaper cares about.
            visible = isWallpaperVisible(foundW);        //该函数决策是否需要将壁纸设为可见;
            if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);

            // If the wallpaper target is animating, we may need to copy
            // its layer adjustment.  Only do this if we are not transfering
            // between two wallpaper targets.
            mWallpaperAnimLayerAdjustment =
                    (mLowerWallpaperTarget == null && foundW.mAppToken != null)
                    ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0;

            final int maxLayer = mPolicy.getMaxWallpaperLayer()
                    * TYPE_LAYER_MULTIPLIER
                    + TYPE_LAYER_OFFSET;

            // Now w is the window we are supposed to be behind...  but we
            // need to be sure to also be behind any of its attached windows,
            // AND any starting window associated with it, AND below the
            // maximum layer the policy allows for wallpapers.
            while (foundI > 0) {        //该循环用于检查找到的“壁纸目标窗口”是否是子窗口,如果是子窗口,那么将foundW指向父窗口,壁纸窗口必须放在父窗口下面;
                WindowState wb = windows.get(foundI-1);
                if (wb.mBaseLayer < maxLayer &&
                        wb.mAttachedWindow != foundW &&
                        (foundW.mAttachedWindow == null ||
                                wb.mAttachedWindow != foundW.mAttachedWindow) &&
                        (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
                                foundW.mToken == null || wb.mToken != foundW.mToken)) {
                    // This window is not related to the previous one in any
                    // interesting way, so stop here.
                    break;
                }
                foundW = wb;
                foundI--;
            }
        } else {
            if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target");
        }

        if (foundW == null && topCurW != null) {    //如果没有找到“壁纸目标窗口”,并且存在壁纸窗口,那么将foundW指向壁纸窗口、foundI指向壁纸窗口的上一个窗口位置;
            // There is no wallpaper target, so it goes at the bottom.
            // We will assume it is the same place as last time, if known.
            foundW = topCurW;
            foundI = topCurI+1;
        } else {
            // Okay i is the position immediately above the wallpaper.  Look at
            // what is below it for later.
            foundW = foundI > 0 ? windows.get(foundI-1) : null;
        }

        if (visible) {
            if (mWallpaperTarget.mWallpaperX >= 0) {
                mLastWallpaperX = mWallpaperTarget.mWallpaperX;
                mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
            }
            if (mWallpaperTarget.mWallpaperY >= 0) {
                mLastWallpaperY = mWallpaperTarget.mWallpaperY;
                mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
            }
            if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
                mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
            }
            if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
                mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
            }
        }

        // Start stepping backwards from here, ensuring that our wallpaper windows
        // are correctly placed.
        int changed = 0;
        int curTokenIndex = mWallpaperTokens.size();        //mWallpaperTokens中保存着壁纸窗口Token,只有一个对象;
        while (curTokenIndex > 0) {
            curTokenIndex--;
            WindowToken token = mWallpaperTokens.get(curTokenIndex);
            if (token.hidden == visible) {
                if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
                        "Wallpaper token " + token + " hidden=" + !visible);
                changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED;
                token.hidden = !visible;
                // Need to do a layout to ensure the wallpaper now has the
                // correct size.
                getDefaultDisplayContentLocked().layoutNeeded = true;
            }

            int curWallpaperIndex = token.windows.size();
            while (curWallpaperIndex > 0) {        //壁纸窗口Token只有一个壁纸窗口;
                curWallpaperIndex--;
                WindowState wallpaper = token.windows.get(curWallpaperIndex);

                if (visible) {
                    updateWallpaperOffsetLocked(wallpaper, dw, dh, false);        //更新壁纸窗口偏移;
                }

                // First, make sure the client has the current visibility
                // state.
                dispatchWallpaperVisibility(wallpaper, visible);            //通知壁纸窗口当前的可见性,如果变为可见,那么壁纸必然需要更新绘制;

                wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
                if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
                        + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);

                // First, if this window is at the current index, then all
                // is well.
                if (wallpaper == foundW) {
                    foundI--;
                    foundW = foundI > 0
                            ? windows.get(foundI-1) : null;
                    continue;
                }

                // The window didn't match...  the current wallpaper window,
                // wherever it is, is in the wrong place, so make sure it is
                // not in the list.
                int oldIndex = windows.indexOf(wallpaper);
                if (oldIndex >= 0) {
                    if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at "
                            + oldIndex + ": " + wallpaper);
                    windows.remove(oldIndex);
                    mWindowsChanged = true;
                    if (oldIndex < foundI) {
                        foundI--;
                    }
                }

                // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
                // layer. For keyguard over wallpaper put the wallpaper under the keyguard.
                int insertionIndex = 0;
                if (visible && foundW != null) {         //如果“目标窗口壁纸”为keyguard,那么直接插入keyguard窗口之下;否则直接放到窗口堆栈最底下;
                    final int type = foundW.mAttrs.type;
                    final int privateFlags = foundW.mAttrs.privateFlags;
                    if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0
                            || type == TYPE_KEYGUARD_SCRIM) {
                        insertionIndex = windows.indexOf(foundW);
                    }
                }
                if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
                    Slog.v(TAG, "Moving wallpaper " + wallpaper
                            + " from " + oldIndex + " to " + insertionIndex);
                }

                windows.add(insertionIndex, wallpaper);
                mWindowsChanged = true;
                changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
            }
        }

        /*
        final TaskStack targetStack =
                mWallpaperTarget == null ? null : mWallpaperTarget.getStack();
        if ((changed & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0 &&
                targetStack != null && !targetStack.isHomeStack()) {
            // If the wallpaper target is not on the home stack then make sure that all windows
            // from other non-home stacks are above the wallpaper.
            for (i = foundI - 1; i >= 0; --i) {
                WindowState win = windows.get(i);
                if (!win.isVisibleLw()) {
                    continue;
                }
                final TaskStack winStack = win.getStack();
                if (winStack != null && !winStack.isHomeStack() && winStack != targetStack) {
                    windows.remove(i);
                    windows.add(foundI + 1, win);
                }
            }
        }
        */

        if (targetChanged && DEBUG_WALLPAPER_LIGHT) {
            Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
                    + " lower=" + mLowerWallpaperTarget + " upper="
                    + mUpperWallpaperTarget);
        }

        return changed;
    }
 类似资料: