Android10 SystemUI系统手势导航

1、Android10 源码编译相关问题

2、Android10 系统进程Zygote启动

3、Android10 系统进程SystemServer

4、Android10 launcher启动流程

5、Android10 系统发送开机广播时机

6、Android10 AppComponentFactory源码梳理

7、Android10 InputManagerService事件输入输出

8、Android10 InputManagerService本地实现

9、Android10 SystemUI系统手势导航


        从Android9开始,android系统就添加了系统导航手势,对于现在高版本的手机,有些系统默认就是开启导航手势的,有些还是沿用以前导航栏,但是也是可以在设置中开启导航手势,导航栏会自动消失,这篇文章主要讲解的是系统导航收拾是如何实现的。

        对于系统导航手势的实现,android10还是在SystemUI中实现的,在以后的版本中可能就是在Launcher3中,SystemUI是一个系统应用,在SystemServer中启动,先来看下是如何启动的:

    private static void startSystemUi(Context context, WindowManagerService windowManager) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                "com.android.systemui.SystemUIService"));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
    }

通过开启SystemUIService服务来拉起SystemUI,这里就直接来看下SystemUIService:

public class SystemUIService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();

        ... ...
    }
}

没做啥,就是继续调用SystemUIApplication.startServicesIfNeeded(),在说这个方法前,可以先了解下Android10 AppComponentFactory源码梳理,这里先来看下Manifest中对application的配置:

    

这里用SystemUIAppComponentFactory替换掉了android系统中默认的appComponentFactory,先来看看SystemUIAppComponentFactory:

public class SystemUIAppComponentFactory extends CoreComponentFactory {

    @Inject
    public ContextComponentHelper mComponentHelper;

    public SystemUIAppComponentFactory() {
        super();
    }

    @Override
    public Application instantiateApplication(ClassLoader cl, String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Application app = super.instantiateApplication(cl, className);
        if (app instanceof SystemUIApplication) {
            ((SystemUIApplication) app).setContextAvailableCallback(
                    context -> {
                        SystemUIFactory.createFromConfig(context);
                        SystemUIFactory.getInstance().getRootComponent().inject(
                                SystemUIAppComponentFactory.this);
                    }
            );
        }

        return app;
    }
}

根据上面的配置,这里的if语句为true,会执行下面的回调语句,这里主要说下这个回调语句是做什么的,在SystemUI应用中,使用了Dagger对很多的实例进行初始化,这里就是对Dagger注入对象进行初始化,这里对appComponentFactory的作用就先到这了,接着上面的开启的服务,就进入到SystemUIApplication.startServicesIfNeeded():

public class SystemUIApplication extends Application implements SysUiServiceProvider {

    public static final String TAG = "SystemUIService";
    private static final boolean DEBUG = false;

    /**
     * Hold a reference on the stuff we start.
     */
    //预先定义的SystemUI组件服务
    private SystemUI[] mServices;
    private boolean mServicesStarted;
    private boolean mBootCompleted;
    //SystemUI组件集合
    private final Map, Object> mComponents = new HashMap<>();
    private ContextAvailableCallback mContextAvailableCallback;

    public SystemUIApplication() {
        super();
        Log.v(TAG, "SystemUIApplication constructed.");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        ... ...
        //在SystemUIAppComponentFactory中设置的回调立马就在这调用了,感觉是多此一举
        //在android 11中就没有通过这个回调了,而是直接在这里初始化的Dagger
        mContextAvailableCallback.onContextAvailable(this);
        ... ...
    }

    /**
     * Makes sure that all the SystemUI services are running. If they are already running, this is a
     * no-op. This is needed to conditinally start all the services, as we only need to have it in
     * the main process.
     * 

This method must only be called from the main thread.

*/ //开启SystemUI组件服务,组件服务预先定义在config_systemUIServiceComponents public void startServicesIfNeeded() { String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names); } private void startServicesIfNeeded(String metricsPrefix, String[] services) { //服务已经开始就直接返回 if (mServicesStarted) { return; } mServices = new SystemUI[services.length]; ... ... //遍历定义的SystemUI组件服务,通过反射创建服务 final int N = services.length; for (int i = 0; i < N; i++) { String clsName = services[i]; if (DEBUG) Log.d(TAG, "loading: " + clsName); log.traceBegin(metricsPrefix + clsName); long ti = System.currentTimeMillis(); Class cls; try { cls = Class.forName(clsName); Object o = cls.newInstance(); if (o instanceof SystemUI.Injector) { o = ((SystemUI.Injector) o).apply(this); } mServices[i] = (SystemUI) o; } catch(ClassNotFoundException ex){ throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } mServices[i].mContext = this; //将全局存放SystemUI组件的集合赋值给服务,服务创建好后会存放到集合中 mServices[i].mComponents = mComponents; if (DEBUG) Log.d(TAG, "running: " + mServices[i]); mServices[i].start(); log.traceEnd(); // Warn if initialization of component takes too long ti = System.currentTimeMillis() - ti; if (ti > 1000) { Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms"); } if (mBootCompleted) { mServices[i].onBootCompleted(); } } ... ... mServicesStarted = true; } @SuppressWarnings("unchecked") public T getComponent(Class interfaceType) { return (T) mComponents.get(interfaceType); } public SystemUI[] getServices() { return mServices; } void setContextAvailableCallback(ContextAvailableCallback callback) { mContextAvailableCallback = callback; } interface ContextAvailableCallback { void onContextAvailable(Context context); } }

整体逻辑还是比较清晰的,startServicesIfNeeded()主要做了三件事:

  1. 获取事先定义在xml中的SystemUI组件服务类;
  2. 遍历服务,通过反射机制创建SystemUI组件服务;
  3. 调用SystemUI的start()方法启动服务;

接下来看下定义的组件服务frameworks/base/packages/SystemUI/res/values/config.xml:

    
    
        com.android.systemui.util.NotificationChannels
        com.android.systemui.statusbar.CommandQueue$CommandQueueStart
        com.android.systemui.keyguard.KeyguardViewMediator
        com.android.systemui.recents.Recents
        com.android.systemui.volume.VolumeUI
        com.android.systemui.stackdivider.Divider
        com.android.systemui.SystemBars
        com.android.systemui.usb.StorageNotification
        com.android.systemui.power.PowerUI
        com.android.systemui.media.RingtonePlayer
        com.android.systemui.keyboard.KeyboardUI
        com.android.systemui.pip.PipUI
        com.android.systemui.shortcut.ShortcutKeyDispatcher
        @string/config_systemUIVendorServiceComponent
        com.android.systemui.util.leak.GarbageMonitor$Service
        com.android.systemui.LatencyTester
        com.android.systemui.globalactions.GlobalActionsComponent
        com.android.systemui.ScreenDecorations
        com.android.systemui.biometrics.BiometricDialogImpl
        com.android.systemui.SliceBroadcastRelayHandler
        com.android.systemui.SizeCompatModeActivityController
        com.android.systemui.statusbar.notification.InstantAppNotifier
        com.android.systemui.theme.ThemeOverlayController
    

这里主要来看下SystemBars,这里主要定义的是系统状态栏以及导航栏:

public class SystemBars extends SystemUI {
    private static final String TAG = "SystemBars";
    private static final boolean DEBUG = false;
    private static final int WAIT_FOR_BARS_TO_DIE = 500;

    // in-process fallback implementation, per the product config
    private SystemUI mStatusBar;

    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
        createStatusBarFromConfig();
    }

    private void createStatusBarFromConfig() {
        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
        final String clsName = mContext.getString(R.string.config_statusBarComponent);
        if (clsName == null || clsName.length() == 0) {
            throw andLog("No status bar component configured", null);
        }
        Class cls = null;
        try {
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {
            throw andLog("Error loading status bar component: " + clsName, t);
        }
        try {
            mStatusBar = (SystemUI) cls.newInstance();
        } catch (Throwable t) {
            throw andLog("Error creating status bar component: " + clsName, t);
        }
        mStatusBar.mContext = mContext;
        mStatusBar.mComponents = mComponents;
        if (mStatusBar instanceof StatusBar) {
            SystemUIFactory.getInstance().getRootComponent()
                    .getStatusBarInjector()
                    .createStatusBar((StatusBar) mStatusBar);
        }
        mStatusBar.start();
        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
    }

}

SystemBars继承自SystemUI,所有定义的组件服务都是继承自SystemUI,一路看下来,和之前的逻辑类似,去xml中获取配置的类文件,反射获取,调用是start()方法启动,先来看下xml中定义的启动的类:

    
    com.android.systemui.statusbar.phone.StatusBar

所以这里就直接进入到了StatusBar.start()方法:

    @Override
    public void start() {
        ... ...
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        ... ...
        //在创建SystemUI组件的时候,有传进来一个集合,这里就是将创建的组建存放到集合中
        putComponent(StatusBar.class, this);
        ... ...
        //这里就是通过WindowManager去创建系统状态栏和导航栏
        createAndAddWindows(result);
        ... ...
    }

继承自SystemUI的都传进了同一个集合,putComponent()将所有创建的组件都存放到同一个集合中了,这样组建之间都是可以互相获取的,这里主要来看下createAndAddWindows():

    public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
        //这里会去创建状态栏view已经导航view
        makeStatusBarView(result);
        //Dependency这里面的类是通过dagger初始化的,上面有说到但是没有跟下去看,感兴趣的可以自己去看下
        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
        //这里将状态栏view添加到window
        mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
    }

    public int getStatusBarHeight() {
        if (mNaturalBarHeight < 0) {
            final Resources res = mContext.getResources();
            mNaturalBarHeight =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        }
        return mNaturalBarHeight;
    }

先来看下这个状态栏高度的定义,这个是定义在系统资源里面,并没有定义在当前SystemUI应用里面,系统资源统一定义在包名是android的应用里面frameworks/base/core/res/res:

    
    @dimen/status_bar_height_portrait
    
    24dp
    
    @dimen/status_bar_height_portrait

当系统不需要状态栏的时候,这里直接设置为0就ok了,说完状态栏的高度,再来看看StatusBarWindowController.add()是如何添加状态栏的:

    public void add(ViewGroup statusBarView, int barHeight) {

        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        mLp = new LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                barHeight,
                LayoutParams.TYPE_STATUS_BAR,
                LayoutParams.FLAG_NOT_FOCUSABLE
                        | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | LayoutParams.FLAG_SPLIT_TOUCH
                        | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.token = new Binder();
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);
        mLpChanged.copyFrom(mLp);
        onThemeChanged();
    }

通过WindowManager的addView()方法将状态栏view添加进去,回到上面的createAndAddWindows()方法,在来看下里面调用的makeStatusBarView(),这里就会去创建状态栏view:

    // ================================================================================
    // Constructing the view
    // ================================================================================
    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
        ... ...
        //这里就是去创建mStatusBarWindow,类型是StatusBarWindowView,继承自FrameLayout
        inflateStatusBarWindow(context);
        ... ...
        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment =
                            (CollapsedStatusBarFragment) fragment;
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                    PhoneStatusBarView oldStatusBarView = mStatusBarView;
                    mStatusBarView = (PhoneStatusBarView) fragment.getView();
                  ... ...  
                }).getFragmentManager()
                .beginTransaction()
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                        CollapsedStatusBarFragment.TAG)
                .commit();
        ... ...
        //这里就是去创建导航栏
        createNavigationBar(result);
        ... ...
    }


    protected void inflateStatusBarWindow(Context context) {
        mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
                LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);
    }

先是通过super_status_bar.xml创建了状态栏的根布局,然后使用Fragment (CollapsedStatusBarFragment)替换布局里面的status_bar_container,对状态栏感兴趣的可以沿着这里继续往下看,我们这里主要是来看下导航栏相关的,也就是createNavigationBar():

    mNavigationBarController = Dependency.get(NavigationBarController.class);
    
    protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
        mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);
    }

只是简单调用NavigationBarController.createNavigationBars(),这个方法内部又是去调用了NavigationBarFragment.create():

    public static View create(Context context, FragmentListener listener) {
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
                PixelFormat.TRANSLUCENT);
        lp.token = new Binder();
        lp.setTitle("NavigationBar" + context.getDisplayId());
        lp.accessibilityTitle = context.getString(R.string.nav_bar);
        lp.windowAnimations = 0;
        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;

        View navigationBarView = LayoutInflater.from(context).inflate(
                R.layout.navigation_bar_window, null);

        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
        if (navigationBarView == null) return null;

        final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
                .create(NavigationBarFragment.class);
        navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {
                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
                fragmentHost.getFragmentManager().beginTransaction()
                        .replace(R.id.navigation_bar_frame, fragment, TAG)
                        .commit();
                fragmentHost.addTagListener(TAG, listener);
            }

            @Override
            public void onViewDetachedFromWindow(View v) {
                FragmentHostManager.removeAndDestroy(v);
            }
        });
        context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
        return navigationBarView;
    }

将创建的navigationBarView添加进window,这样导航栏就可以显示出来,注意上面的fragment,使用NavigationBarFragment创建的view替换了navigaiton_bar_frame,所有这里直接NavigationBarFragment所创建的view:

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.navigation_bar, container, false);
    }

实际导航栏的布局就是这里的navigation_bar.xml了,这里来看下这个布局:



    

关于导航栏的布局,定义在NavigationBarInflaterView中,在NavigationBarInflaterView布局填充完会调用到onFinishInflate(),在这里会去添加横屏、竖屏的真正的显示的导航栏view,这里就不跟进去看了,这里来看看NavigationBarView的构造方法:

    //默认导航模式,屏幕底部三个按钮显示
    private int mNavBarMode = NAV_BAR_MODE_3BUTTON;

    public NavigationBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        ... ...
        mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
        //当前是否是手势导航
        boolean isGesturalMode = isGesturalMode(mNavBarMode);
        ... ...
        // 这个类会与launcher3进行通信,一些逻辑的判断需要借助这个类,在以后的版本中也是借助这个类
        // 来处理返回事件等
        mOverviewProxyService = Dependency.get(OverviewProxyService.class);
        ... ...
        //手势导航就是通过下面这个类来控制
        mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
        ... ...
    }

    @Override
    public void onNavigationModeChanged(int mode) {
        ... ...
        mBarTransitions.onNavigationModeChanged(mNavBarMode);
        mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode, curUserCtx);
        ... ...
    }

当mNavBarMode的值是NAV_BAR_MODE_GESTURAL,这个时候就是启用手势导航,屏幕底部的三个按钮的导航就会消失,这个时候EdgeBackGestureHandler就该其作用了,导航模式改变的时候会调用到onNavigationModeChanged(),这里假设启用系统手势,那这里传进去的就是NAV_BAR_MODE_GESTURAL,这里就来看下EdgeBackGestureHandler.onNavigationModeChanged():

    public void onNavigationModeChanged(int mode, Context currentUserContext) {
        mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
        //更新手势模式
        updateIsEnabled();
        updateCurrentUserResources(currentUserContext.getResources());
    }

    private void updateIsEnabled() {
        boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
        //手势模式已经可用,直接返回
        if (isEnabled == mIsEnabled) {
            return;
        }
        mIsEnabled = isEnabled;
        disposeInputChannel();
        //判断之前的手势view是否还在,还在的话就先清理掉
        if (mEdgePanel != null) {
            mWm.removeView(mEdgePanel);
            mEdgePanel = null;
            mRegionSamplingHelper.stop();
            mRegionSamplingHelper = null;
        }
        //这里假设开启手势,mIsEnable=true
        if (!mIsEnabled) {
            ... ...

        } else {
            ... ...
            try {
                ... ...
                // 监听页面设置手势排除的区域范围,当开启手势后,有可能会与当前页面定义的手势
                // 产生冲突,这是时候就可以通过View的setSystemGestureExclusionRects()
                // 来设置页面将系统手势排除在外
                WindowManagerGlobal.getWindowManagerService()
                        .registerSystemGestureExclusionListener(
                                mGestureExclusionListener, mDisplayId);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to register window manager callbacks", e);
            }

            // 注册输入事件监听,最终会调用到当前类的onInputEvent()方法
            mInputMonitor = InputManager.getInstance().monitorGestureInput(
                    "edge-swipe", mDisplayId);
            mInputEventReceiver = new SysUiInputEventReceiver(
                    mInputMonitor.getInputChannel(), Looper.getMainLooper());

            // 创建手势监听的View,并添加到window
            mEdgePanel = new NavigationBarEdgePanel(mContext);
            mEdgePanelLp = new WindowManager.LayoutParams(
                    mContext.getResources()
                            .getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
                    mContext.getResources()
                            .getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                    PixelFormat.TRANSLUCENT);
            mEdgePanelLp.privateFlags |=
                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
            mEdgePanelLp.setTitle(TAG + mDisplayId);
            mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
            mEdgePanelLp.windowAnimations = 0;
            mEdgePanel.setLayoutParams(mEdgePanelLp);
            mWm.addView(mEdgePanel, mEdgePanelLp);
            ... ...
        }
    }

    class SysUiInputEventReceiver extends InputEventReceiver {
        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
            super(channel, looper);
        }

        public void onInputEvent(InputEvent event) {
            EdgeBackGestureHandler.this.onInputEvent(event);
            finishInputEvent(event, true);
        }
    }

这里的逻辑可以分为两部分:

  1. 注册事件输入通道,可以接收系统的输入事件;
  2. 创建一个支持手势的mEdgePanel view,并添加到window中;

当有输入事件时,EdgeBackGestureHandler的onInputEvent()就会接受到输入事件:

    private void onInputEvent(InputEvent ev) {
        if (ev instanceof MotionEvent) {
            onMotionEvent((MotionEvent) ev);
        }
    }

    private void onMotionEvent(MotionEvent ev) {
        int action = ev.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            //校验当前点击区域是否允许系统手势
            int stateFlags = mOverviewProxyService.getSystemUiStateFlags();
            mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
            mInRejectedExclusion = false;
            mAllowGesture = !QuickStepContract.isBackGestureDisabled(stateFlags)
                    && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
            if (mAllowGesture) {
                mEdgePanelLp.gravity = mIsOnLeftEdge
                        ? (Gravity.LEFT | Gravity.TOP)
                        : (Gravity.RIGHT | Gravity.TOP);
                mEdgePanel.setIsLeftPanel(mIsOnLeftEdge);
                //将事件传递给手势view
                mEdgePanel.handleTouch(ev);
                updateEdgePanelPosition(ev.getY());
                //刷新window的layout属性
                mWm.updateViewLayout(mEdgePanel, mEdgePanelLp);
                mRegionSamplingHelper.start(mSamplingRect);

                mDownPoint.set(ev.getX(), ev.getY());
                mThresholdCrossed = false;
            }
        } else if (mAllowGesture) {
            //允许后续的手势事件,下面的语句只会执行一次
            if (!mThresholdCrossed) {
                //如果是多指就取消手势事件
                if (action == MotionEvent.ACTION_POINTER_DOWN) {
                    // We do not support multi touch for back gesture
                    cancelGesture(ev);
                    return;
                } else if (action == MotionEvent.ACTION_MOVE) {
                    //按下到第一次滑动的时间超过规定时间,取消手势事件
                    if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
                        cancelGesture(ev);
                        return;
                    }
                    float dx = Math.abs(ev.getX() - mDownPoint.x);
                    float dy = Math.abs(ev.getY() - mDownPoint.y);
                    //竖向滑动距离大于横向滑动距离,取消手势
                    if (dy > dx && dy > mTouchSlop) {
                        cancelGesture(ev);
                        return;

                    } else if (dx > dy && dx > mTouchSlop) {
                        mThresholdCrossed = true;
                        // 捕获输入,这一次输入的后续所有事件都传过来
                        mInputMonitor.pilferPointers();
                    }
                }

            }

            // 手势view处理touch事件,mEdgePanel就可以去绘制手势动画了
            mEdgePanel.handleTouch(ev);

            boolean isUp = action == MotionEvent.ACTION_UP;
            if (isUp) {
                boolean performAction = mEdgePanel.shouldTriggerBack();
                // 手抬起来的时候触发返回事件
                if (performAction) {
                    // Perform back
                    sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
                    sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
                }
                ... ...
            }
            ... ...
        }
    }

关于手势NavigationBarEdgePanel这里就不再去看了,是一个继承自view的自定义view,到这里,系统手势导航的整个流程就走完了。

总结:

        整一个流程下来,不管是状态栏还是导航栏,其实现方式都是通过window添加view的方式,只是window的type类型有区别,导航栏这一部分,新增了手势导航,手势部分只是多加输入事件的监听以及处理。

你可能感兴趣的:(android系统,android)