Android 12系统源码_SystemUI(九)WindowInsetsController源码解析

前言

上一篇我们具体分析了在Android系统中SystemUIVisibility属性是如何控制状态栏和导航栏样式的,除了通过SystemUIVisibility属性控制状态栏和导航栏属性以外,我们还可以通过WindowInsetsController相关的类来控制状态栏和导航栏。在Android10以后,谷歌不再推荐使用SystemUIVisibility属性来控制状态栏和导航栏,而是推荐通过和WindowInsetsController相关的类来控制状态栏和导航栏。本篇文章我们就来具体分析一下WindowInsetsController是如何控制状态栏和导航栏的。

一、对状态栏导航栏进行隐藏和显示

1、在Android10以上的系统中,我们可以通过调用以下代码来隐藏状态栏和导航栏。

   //获取WindowInsetsController对象实例
    WindowInsetsController windowInsetsController = getWindow().getDecorView().getWindowInsetsController();
    //调用hide隐藏状态栏
    windowInsetsController.hide(WindowInsets.Type.statusBars())
    //调用hide隐藏导航栏
    windowInsetsController.hide(WindowInsets.Type.navigationBar())
    //调用show显示状态栏
    windowInsetsController.show(WindowInsets.Type.statusBars())
    //调用show显示导航栏
    windowInsetsController.show(WindowInsets.Type.navigationBar())

2、WindowInsets.Type是一个很关键的类,系统就是用它来表示不同的窗口装饰区域类型的,该类使用二进制整数的每一位来标识特定的窗口装饰区域类型,相关的代码如下所示。

frameworks/base/core/java/android/view/WindowInsets.java

public final class WindowInsets {
    
    
    public static final class Type {
    
    
        
        static final int FIRST = 1 << 0;
        static final int STATUS_BARS = FIRST; //状态栏类型
        static final int NAVIGATION_BARS = 1 << 1; //导航类类型
        static final int CAPTION_BAR = 1 << 2;

        static final int IME = 1 << 3;//输入法

        static final int SYSTEM_GESTURES = 1 << 4;
        static final int MANDATORY_SYSTEM_GESTURES = 1 << 5;
        static final int TAPPABLE_ELEMENT = 1 << 6;

        static final int DISPLAY_CUTOUT = 1 << 7;

        static final int LAST = 1 << 8;
        static final int SIZE = 9;
        static final int WINDOW_DECOR = LAST;
         /**
         * 状态栏
         */
        public static @InsetsType int statusBars() {
    
    
            return STATUS_BARS;
        }

        /**
         * 导航类
         */
        public static @InsetsType int navigationBars() {
    
    
            return NAVIGATION_BARS;
        }

        public static @InsetsType int captionBar() {
    
    
            return CAPTION_BAR;
        }

        public static @InsetsType int systemBars() {
    
    
            return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR;
        } 
    }
}

二、WindowInsetsController对象实例的获取。

1、调用DecorView的getWindowInsetsController方法获取实例对象

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    
     
     private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
  
    /**
     * 当前视图是否已经被添加到窗口上
     */
    public boolean isAttachedToWindow() {
    
    
        return mAttachInfo != null;
    }

   @Override
    public WindowInsetsController getWindowInsetsController() {
    
    
        //判断视图是否已经被添加到Window上
        if (isAttachedToWindow()) {
    
    
            return super.getWindowInsetsController();//主要分析是这种情况
        } else {
    
    
            return mPendingInsetsController;
        }
    }
}

如果视图已经被添加到窗口上则继续调用父类的方法,如果视图还未被添加到窗口上,则返回PendingInsetsController对象实例,由于PendingInsetsController对象只是做了个缓存,其内部的实现方式基本和第一种情况一样。这里我们主要分析视图已经被添加到窗口上的这种情况,这个时候会调用父类的getWindowInsetsController方法。

2、getWindowInsetsController方法最初是在View类中实现的。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    
    

    AttachInfo mAttachInfo;
    
    public @Nullable WindowInsetsController getWindowInsetsController() {
    
    
        if (mAttachInfo != null) {
    
    //判断视图是否已经被添加到Window上
            return mAttachInfo.mViewRootImpl.getInsetsController();//调用ViewRootImpl的getInsetsController方法
        }
        ViewParent parent = getParent();
        if (parent instanceof View) {
    
    
            return ((View) parent).getWindowInsetsController();
        } else if (parent instanceof ViewRootImpl) {
    
    
            // Between WindowManager.addView() and the first traversal AttachInfo isn't set yet.
            return ((ViewRootImpl) parent).getInsetsController();
        }
        return null;
    }
}

如果视图已经被添加到窗口上,则最终调用的其实是ViewRootImpl的getInsetsController方法。

3、ViewRootImpl和getInsetsController方法相关的代码如下所示。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
    
    
     private final InsetsController mInsetsController;
    public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
    
    
        ...代码省略...
        mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
        ...代码省略...
  }
   public InsetsController getInsetsController() {
    
    
        return mInsetsController;
    }
}

通过getWindow().getDecorView().getWindowInsetsController()方法所获取的对象实例其实是ViewRootImpl对象中类型为的InsetsController的属性对象mInsetsController,而该对象最初是在ViewRootImpl的构造方法中被赋值的。

三、调用hide隐藏状态栏。

1、由于隐藏和显示状态栏导航栏的流程大同小异,这里我们将会结合源码来梳理InsetsController隐藏状态栏的相关流程。

    //调用hide隐藏状态栏
    windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())

结合前面的代码我们知道,这里调用的其实是InsetsController的hide方法,WindowInsetsCompat.Type.statusBars方法返回的是STATUS_BARS类型。

2、下面我们具体来分析InsetsController类的hide方法。

frameworks/base/core/java/android/view/InsetsController.java

public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
    
    
  
  public void hide(int types) {
    
    
        this.hide(types, false);
    }

    public void hide(@InsetsType int types, boolean fromIme) {
    
    
        int typesReady = 0;
        //将一个外部类型转化为内部类型,其实就是将WindowInsets.Type类型对应的整数转化为InsetsState类型对应的整数,
        //返回InsetsState类型对应的ArraySet<Integer>类型的集合。
        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
        //遍历internalTypes
        for (int i = internalTypes.size() - 1; i >= 0; i--) {
    
    
            @InternalInsetsType int internalType = internalTypes.valueAt(i);
            //获取当前internalType对应的动画
            @AnimationType int animationType = getAnimationType(internalType);
            //获取当前internalType对应的窗口装饰区域事件消费者
            InsetsSourceConsumer consumer = getSourceConsumer(internalType);
            if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
                    || animationType == ANIMATION_TYPE_HIDE) {
    
    
                //如果动画类型为隐藏类型、或者视图属于隐藏类型且没有隐藏动画的,直接跳过
                continue;
            }
            //否则会将这个内部类型(InsetsState类型)对应的整数转化为外部类型(WindowInsets.Type类型)对应的整数
            typesReady |= InsetsState.toPublicType(consumer.getType());
        }
        //继续调用applyAnimation方法
        applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
    }

}

以上代码主要做了以下几点。
1)首先会调用InsetsState类的toInternalType方法,将WindowInsets.Type类型对应的整数转化为InsetsState类型对应的整数,并封装成ArraySet类型的集合返回。InsetsState的toInternalType方法如下所示。

frameworks/base/core/java/android/view/InsetsState.java

public class InsetsState implements Parcelable {
    
    
    public static final int ITYPE_INVALID = -1;

    static final int FIRST_TYPE = 0;

    public static final int ITYPE_STATUS_BAR = FIRST_TYPE;//状态栏
    public static final int ITYPE_NAVIGATION_BAR = 1;//导航栏
    public static final int ITYPE_CAPTION_BAR = 2;

    public static final int ITYPE_TOP_GESTURES = 3;
    public static final int ITYPE_BOTTOM_GESTURES = 4;
    public static final int ITYPE_LEFT_GESTURES = 5;
    public static final int ITYPE_RIGHT_GESTURES = 6;

    public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
    public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
    public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
    public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;

    public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
    public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
    public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
    public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;

    public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
    public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;

    public static final int ITYPE_IME = 19;//输入法

    public static final int ITYPE_CLIMATE_BAR = 20;
    public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;

    static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
    public static final int SIZE = LAST_TYPE + 1;
    
    public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) {
    
    
        final ArraySet<Integer> result = new ArraySet<>();
        if ((types & Type.STATUS_BARS) != 0) {
    
    
            result.add(ITYPE_STATUS_BAR);
            result.add(ITYPE_CLIMATE_BAR);
        }
        if ((types & Type.NAVIGATION_BARS) != 0) {
    
    
            result.add(ITYPE_NAVIGATION_BAR);
            result.add(ITYPE_EXTRA_NAVIGATION_BAR);
        }
        if ((types & Type.CAPTION_BAR) != 0) {
    
    
            result.add(ITYPE_CAPTION_BAR);
        }
        if ((types & Type.SYSTEM_GESTURES) != 0) {
    
    
            result.add(ITYPE_LEFT_GESTURES);
            result.add(ITYPE_TOP_GESTURES);
            result.add(ITYPE_RIGHT_GESTURES);
            result.add(ITYPE_BOTTOM_GESTURES);
        }
        if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) {
    
    
            result.add(ITYPE_LEFT_MANDATORY_GESTURES);
            result.add(ITYPE_TOP_MANDATORY_GESTURES);
            result.add(ITYPE_RIGHT_MANDATORY_GESTURES);
            result.add(ITYPE_BOTTOM_MANDATORY_GESTURES);
        }
        if ((types & Type.DISPLAY_CUTOUT) != 0) {
    
    
            result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
            result.add(ITYPE_TOP_DISPLAY_CUTOUT);
            result.add(ITYPE_RIGHT_DISPLAY_CUTOUT);
            result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT);
        }
        if ((types & Type.IME) != 0) {
    
    
            result.add(ITYPE_IME);
        }
        return result;
    }
 }

由于WindowInsets.Type是用二进制的每一位来标识的类型的,而InsetsState是整数,这样同一个WindowInsets.Type可能会对应多个InsetsState类型的整数,因此这里将它们封装成了ArraySet类型的集合。

2)遍历通过InsetsState.toInternalType方法所得到的类型为InsetsState类型的整数集合internalTypes 。

3)调用getAnimationType方法获取当前InsetsState类型的整数(即窗口装饰区域类型)对应的动画类型。

public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
    
    
    /** Not running an animation. */
    public static final int ANIMATION_TYPE_NONE = -1;
    /** Running animation will show insets */
    public static final int ANIMATION_TYPE_SHOW = 0;
    /** Running animation will hide insets */
    public static final int ANIMATION_TYPE_HIDE = 1;
    /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */
    public static final int ANIMATION_TYPE_USER = 2;
    /** Running animation will resize insets */
    public static final int ANIMATION_TYPE_RESIZE = 3;
    private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
    
    public @AnimationType int getAnimationType(@InternalInsetsType int type) {
    
    
        for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
    
    
            InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
            if (control.controlsInternalType(type)) {
    
    
                //如果存在缓存直接使用缓存
                return mRunningAnimations.get(i).type;
            }
        }
        //否则返回ANIMATION_TYPE_NONE,表示没有动画
        return ANIMATION_TYPE_NONE;
    }
   
}

4)调用getSourceConsumer方法获取当前InsetsState类型的整数(即窗口装饰区域类型)对应的消费者。

public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
    
    

    private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
    private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
   
    public InsetsController(Host host) {
    
    
        this(host, (controller, type) -> {
    
    
            if (type == ITYPE_IME) {
    
    
            	//是输入法,此类继承自InsetsSourceConsumer
                return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
            } else {
    
    
                //不是输入法
                return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
                        controller);
            }
        }, host.getHandler());
    }    
    public InsetsController(Host host,
            BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
            Handler handler) {
    
    
        mHost = host;
        mConsumerCreator = consumerCreator;
        ...代码省略...
	}
	
    public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) {
    
    
        InsetsSourceConsumer controller = mSourceConsumers.get(type);
        if (controller != null) {
    
    
            //如果存在缓存直接使用缓存
            return controller;
        }
        //接受类型为InsetsController和Integer类型的参数,创建InsetsSourceConsumer对象实例
        controller = mConsumerCreator.apply(this, type);
        //缓存
        mSourceConsumers.put(type, controller);
        return controller;
    }

}
public interface BiFunction<T, U, R> {
    
    
    /**
     * 此方法将t、u作为参数,创建R类型的对象实例。
     */
    R apply(T t, U u);
}

getSourceConsumer方法会调用BiFunction类型的mConsumerCreator对象的apply方法,该方法会创建InsetsSourceConsumer对象实例。
mConsumerCreator对象最早是在InsetsController的构造方法中被赋值的,该方法会根据传入的不同type类型,决定返回的是ImeInsetsSourceConsumer对象还是InsetsSourceConsumer对象,然后将该对象缓存到mSourceConsumers中,然后返回。

5)如果动画类型为隐藏类型、或者视图属于隐藏类型且没有隐藏动画的,直接跳过,否则会调用InsetsState的toPublicType方法将这个内部类型(InsetsState类型)对应的整数重新转化为外部类型(WindowInsets.Type类型)对应的整数。

public class InsetsState implements Parcelable {
    
    
    public static final int ITYPE_INVALID = -1;

    static final int FIRST_TYPE = 0;

    public static final int ITYPE_STATUS_BAR = FIRST_TYPE;//状态栏
    public static final int ITYPE_NAVIGATION_BAR = 1;//导航栏
    public static final int ITYPE_CAPTION_BAR = 2;

    public static final int ITYPE_TOP_GESTURES = 3;
    public static final int ITYPE_BOTTOM_GESTURES = 4;
    public static final int ITYPE_LEFT_GESTURES = 5;
    public static final int ITYPE_RIGHT_GESTURES = 6;

    public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
    public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
    public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
    public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;

    public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
    public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
    public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
    public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;

    public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
    public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;

    public static final int ITYPE_IME = 19;//输入法

    public static final int ITYPE_CLIMATE_BAR = 20;
    public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;

    static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
    public static final int SIZE = LAST_TYPE + 1;
    /**
     * 将InsetsState类型对应的整数转化为WindowInsets.Type类型对应的整数
     */
    public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
    
    
        switch (type) {
    
    
            case ITYPE_STATUS_BAR:
            case ITYPE_CLIMATE_BAR:
                return Type.STATUS_BARS;//WindowInsets.Type类型的状态栏
            case ITYPE_NAVIGATION_BAR:
            case ITYPE_EXTRA_NAVIGATION_BAR:
                return Type.NAVIGATION_BARS;//WindowInsets.Type类型导航栏
            case ITYPE_CAPTION_BAR:
                return Type.CAPTION_BAR;//WindowInsets.Type类型
            case ITYPE_IME:
                return Type.IME;//WindowInsets.Type类型输入法
            case ITYPE_TOP_MANDATORY_GESTURES:
            case ITYPE_BOTTOM_MANDATORY_GESTURES:
            case ITYPE_LEFT_MANDATORY_GESTURES:
            case ITYPE_RIGHT_MANDATORY_GESTURES:
                return Type.MANDATORY_SYSTEM_GESTURES;
            case ITYPE_TOP_GESTURES:
            case ITYPE_BOTTOM_GESTURES:
            case ITYPE_LEFT_GESTURES:
            case ITYPE_RIGHT_GESTURES:
                return Type.SYSTEM_GESTURES;
            case ITYPE_LEFT_TAPPABLE_ELEMENT:
            case ITYPE_TOP_TAPPABLE_ELEMENT:
            case ITYPE_RIGHT_TAPPABLE_ELEMENT:
            case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
                return Type.TAPPABLE_ELEMENT;
            case ITYPE_LEFT_DISPLAY_CUTOUT:
            case ITYPE_TOP_DISPLAY_CUTOUT:
            case ITYPE_RIGHT_DISPLAY_CUTOUT:
            case ITYPE_BOTTOM_DISPLAY_CUTOUT:
                return Type.DISPLAY_CUTOUT;
            default:
                throw new IllegalArgumentException("Unknown type: " + type);
        }
    }
}

这样其实就过滤掉了动画类型为隐藏类型,或者视图属于隐藏类型且没有隐藏动画的。

6)继续调用applyAnimation方法

3、InsetsController的applyAnimation方法如下所示。

public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
    
    

      public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
    
    
        boolean skipAnim = false;//默认跳过动画
        if ((types & ime()) != 0) {
    
    //如果是输入法则可能不跳过动画
            final InsetsSourceConsumer consumer = mSourceConsumers.get(ITYPE_IME);
            final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
            // Skip showing animation once that made by system for some reason.
            // (e.g. starting window with IME snapshot)
            if (imeControl != null) {
    
    
                skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
                        && consumer.hasViewFocusWhenWindowFocusGain();
            }
        }
        //继续调用applyAnimation
        applyAnimation(types, show, fromIme, skipAnim);
    }
    
    public void applyAnimation(int types, boolean show, boolean fromIme, boolean skipAnim) {
    
    
        if (types != 0) {
    
    
            //是否有动画回调
            boolean hasAnimationCallbacks = this.mHost.hasAnimationCallbacks();
            //内部动画控制器监听者
            InsetsController.InternalAnimationControlListener listener = new InsetsController.InternalAnimationControlListener(show, hasAnimationCallbacks, types, this.mHost.getSystemBarsBehavior(), skipAnim || this.mAnimationsDisabled, this.mHost.dipToPx(-80));
            //继续调用controlAnimationUnchecked方法
            this.controlAnimationUnchecked(types, (CancellationSignal)null, listener, (Rect)null, fromIme, listener.getDurationMs(), listener.getInsetsInterpolator(), show ? 0 : 1, show ? 0 : 1, !hasAnimationCallbacks);
        }
    }

    private void controlAnimationUnchecked(int types, CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, int animationType, int layoutInsetsDuringAnimation, boolean useInsetsAnimationThread) {
    
    
            ...代码省略...
          //继续调用updateRequestedVisibilities方法
           this.updateRequestedVisibilities();
            ...代码省略...
    }

}

经过层层调用,最终会调用InsetsController的updateRequestedVisibilities方法。
4、updateRequestedVisibilities方法如下所示。

public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
    
    

private final InsetsController.Host mHost;
    
 public InsetsController(InsetsController.Host host) {
    
    
        this(host, (controller, type) -> {
    
    
            return (InsetsSourceConsumer)(type == 19 ? new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller) : new InsetsSourceConsumer(type, controller.mState, Transaction::new, controller));
        }, host.getHandler());
    }

    public InsetsController(InsetsController.Host host, BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator, Handler handler) {
    
    
        ...代码省略...
        this.mHost = host;
        ...代码省略...
    }


    private void updateRequestedVisibilities() {
    
    
        boolean changed = false;
        for(int i = this.mRequestedVisibilityChanged.size() - 1; i >= 0; --i) {
    
    
            InsetsSourceConsumer consumer = (InsetsSourceConsumer)this.mRequestedVisibilityChanged.valueAt(i);
            int type = consumer.getType();
            if (type != 2) {
    
    
                boolean requestedVisible = consumer.isRequestedVisible();
                if (this.mRequestedVisibilities.getVisibility(type) != requestedVisible) {
    
    
                    this.mRequestedVisibilities.setVisibility(type, requestedVisible);
                    changed = true;
                }
            }
        }

        this.mRequestedVisibilityChanged.clear();
        if (changed) {
    
    
            //调用mHost的updateRequestedVisibilities方法
            this.mHost.updateRequestedVisibilities(this.mRequestedVisibilities);
        }
    }
}

最终会调用类型为mHost的updateRequestedVisibilities方法,而mHost是在构造方法中被赋值的,结合ViewRootImpl的构造方法我们知道这里的传入的对象实例是ViewRootInsetsControllerHost。

5、ViewRootInsetsControllerHost的updateRequestedVisibilities方法如下所示。

public class ViewRootInsetsControllerHost implements InsetsController.Host {
    
    
    @Override
    public void updateRequestedVisibilities(InsetsVisibilities vis) {
    
    
        try {
    
    
            if (mViewRoot.mAdded) {
    
    
                //调用ViewRootImpl所对应的窗口会话的updateRequestedVisibilities方法
                mViewRoot.mWindowSession.updateRequestedVisibilities(mViewRoot.mWindow, vis);
            }
        } catch (RemoteException e) {
    
    
            Log.e(TAG, "Failed to call insetsModified", e);
        }
    }
}

ViewRootInsetsControllerHost的updateRequestedVisibilities方法最终会调用ViewRootImpl所对应的窗口Session的updateRequestedVisibilities方法。

6、Session的updateRequestedVisibilities方法如下所示。

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    
    
    @Override
    public void updateRequestedVisibilities(IWindow window, InsetsVisibilities visibilities) {
    
    
        synchronized (mService.mGlobalLock) {
    
    
            final WindowState windowState = mService.windowForClientLocked(this, window,
                    false /* throwOnError */);
            if (windowState != null) {
    
    
                windowState.setRequestedVisibilities(visibilities);
                //继续调用
                windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState);
            }
        }
    }
}

Session的updateRequestedVisibilities方法会调用WindowState.getDisplayContent().getInsetsPolicy()方法获取InsetsPolicy对象实例,然后调用该对象的onInsetsModified方法。
7、InsetsPolicy的onInsetsModified方法如下所示。

class InsetsPolicy {
    
    

    private final InsetsStateController mStateController;

    void onInsetsModified(InsetsControlTarget caller) {
    
    
        //调用InsetsStateController的onInsetsModified方法。
        mStateController.onInsetsModified(caller);
        checkAbortTransient(caller);
        updateBarControlTarget(mFocusedWin);
    }

}

8、InsetsStateController 的onInsetsModified方法如下所示。

class InsetsStateController{
    
    

    void onInsetsModified(InsetsControlTarget caller) {
    
    
        boolean changed = false;
        for (int i = mProviders.size() - 1; i >= 0; i--) {
    
    
            changed |= mProviders.valueAt(i).updateClientVisibility(caller);
        }
        if (changed) {
    
    
            notifyInsetsChanged();
            mDisplayContent.updateSystemGestureExclusion();
            //调用DisplayPolicy的updateSystemBarAttributes方法
            mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
        }
    }

}

9、DisplayPolicy的updateSystemBarAttributes方法如下所示。

public class DisplayPolicy {
    
    
    void updateSystemBarAttributes() {
    
    
        WindowState winCandidate = mFocusedWindow;
        if (winCandidate == null && mTopFullscreenOpaqueWindowState != null
                && (mTopFullscreenOpaqueWindowState.mAttrs.flags
                & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) {
    
    
            // Only focusable window can take system bar control.
            winCandidate = mTopFullscreenOpaqueWindowState;
        }
        // If there is no window focused, there will be nobody to handle the events
        // anyway, so just hang on in whatever state we're in until things settle down.
        if (winCandidate == null) {
    
    
            return;
        }

        // The immersive mode confirmation should never affect the system bar visibility, otherwise
        // it will unhide the navigation bar and hide itself.
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
    
    

            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
            // keys, we let it keep controlling the visibility.
            final boolean lastFocusCanReceiveKeys =
                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
            winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade
                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
                            : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
    
    
                return;
            }
        }
        final WindowState win = winCandidate;
        mSystemUiControllingWindow = win;
        final int displayId = getDisplayId();
        final int disableFlags = win.getDisableFlags();
        final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
        final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
                mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
        final boolean isNavbarColorManagedByIme =
                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
        final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
                navColorWin) | opaqueAppearance;
        final int behavior = win.mAttrs.insetsFlags.behavior;
        final String focusedApp = win.mAttrs.packageName;
        final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
                || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
        final AppearanceRegion[] appearanceRegions =
                new AppearanceRegion[mStatusBarColorWindows.size()];
        for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
    
    
            final WindowState windowState = mStatusBarColorWindows.get(i);
            appearanceRegions[i] = new AppearanceRegion(
                    getStatusBarAppearance(windowState, windowState),
                    new Rect(windowState.getFrame()));
        }
        if (mLastDisableFlags != disableFlags) {
    
    
            mLastDisableFlags = disableFlags;
            final String cause = win.toString();
            callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
                    cause));
        }
        if (mLastAppearance == appearance
                && mLastBehavior == behavior
                && mRequestedVisibilities.equals(win.getRequestedVisibilities())
                && Objects.equals(mFocusedApp, focusedApp)
                && mLastFocusIsFullscreen == isFullscreen
                && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
    
    
            return;
        }
        if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
                && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
    
    
            mService.mInputManager.setSystemUiLightsOut(
                    isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
        }
        final InsetsVisibilities requestedVisibilities =
                new InsetsVisibilities(win.getRequestedVisibilities());
        mLastAppearance = appearance;
        mLastBehavior = behavior;
        mRequestedVisibilities = requestedVisibilities;
        mFocusedApp = focusedApp;
        mLastFocusIsFullscreen = isFullscreen;
        mLastStatusBarAppearanceRegions = appearanceRegions;
        callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
                appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
                requestedVisibilities, focusedApp));
    }
}

猜你喜欢

转载自blog.csdn.net/abc6368765/article/details/129982740
今日推荐