系统设置中会设置手势导航模式 在Setting中可以进行设置查看原生代码发现系统导航类设置为?SystemNavigationGestureSettings 其中SystemNavigationGestureSettings是Preference定义在accessibility_settings.xml中在XML中对应的控制类是SystemNavigationPreferenceController通过代码查看到SystemNavigationGestureSettings.java继承之RadioButtonPickerFragment
涉及路径:
packages/apps/Settings/src/com/android/settings/widget/RadioButtonPickerFragment.java
packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
frameworks/base/core/java/android/content/om/OverlayManager.java
frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
我们先查看RadioButtonPickerFragment.java 中的点击按钮的实现,通过?setDefaultKey
抽象类调用到SystemNavigationGestureSettings中的setDefaultKey方法。
/packages/apps/Settings/src/com/android/settings/widget/RadioButtonPickerFragment.java
@Override
public void onRadioButtonClicked(RadioButtonPreference selected) {
final String selectedKey = selected.getKey();
onRadioButtonConfirmed(selectedKey);
}
protected void onRadioButtonConfirmed(String selectedKey) {
//执行SystemNavigationGestureSettings 中的setDefaultKey方法
final boolean success = setDefaultKey(selectedKey);
if (success) {
//更新按钮
updateCheckedState(selectedKey);
}
onSelectionPerformed(success);
}
//抽象方法,调用子类
protected abstract boolean setDefaultKey(String key);
SystemNavigationGestureSettings.java中的setDefaultKey方法
packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@Override
protected boolean setDefaultKey(String key) {
//通过使用overlaymanager设置对应不同的模式
setCurrentSystemNavigationMode(mOverlayManager, key);
//根据不同的navigationmode 设置不同的video资源
setIllustrationVideo(mVideoPreference, key);
//打开教程说明
if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && (
isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
return true;
}
setCurrentSystemNavigationMode方法为手势导航和导航按钮两件三件的切换
static void setCurrentSystemNavigationMode(IOverlayManager overlayManager, String key) {
String overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY;
switch (key) {
case KEY_SYSTEM_NAV_GESTURAL:
//手势导航
overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY;
break;
case KEY_SYSTEM_NAV_2BUTTONS:
//两个按键
overlayPackage = NAV_BAR_MODE_2BUTTON_OVERLAY;
break;
case KEY_SYSTEM_NAV_3BUTTONS:
//三个按键
overlayPackage = NAV_BAR_MODE_3BUTTON_OVERLAY;
break;
}
try {
//按键之前的切换
overlayManager.setEnabledExclusiveInCategory(overlayPackage, USER_CURRENT);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
接了下来我我们查看按键切换的OverlayManager.java类 frameworks/base/core/java/android/content/om/OverlayManager.java
public void setEnabledExclusiveInCategory(@NonNull final String packageName,
@NonNull UserHandle user) throws SecurityException, IllegalStateException {
try {
//执行 IOverlayManager.aidl中setEnabledExclusiveInCategory方法实际是OverlayManagerService.java
if (!mService.setEnabledExclusiveInCategory(packageName, user.getIdentifier())) {
throw new IllegalStateException("setEnabledExclusiveInCategory failed");
}
} catch (SecurityException e) {
rethrowSecurityException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
可以看到mService.setEnabledExclusiveInCategory 中mService 指的是?IOverlayManager.aidl实现他的地方是OverlayManagerService.java frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java
private final IBinder mService = new IOverlayManager.Stub() {
//省略代码
@Override
public boolean setEnabledExclusiveInCategory(@Nullable String packageName,
final int userIdArg) {
try {
//省略代码
try {
synchronized (mLock) {
//转到OverlayManagerServiceImpl.java中
return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
realUserId);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
} finally {
traceEnd(TRACE_TAG_RRO);
}
}
//省略代码
}
mImpl.setEnabledExclusive 重的 mImpl 指的是OverlayManagerServiceImpl.Java 中的接口方法
interface OverlayChangeListener {
/**
* An event triggered by changes made to overlay state or settings as well as changes that
* add or remove target packages of overlays.
**/
void onOverlaysChanged(@NonNull String targetPackage, int userId);
}
实现 OverlayChangeListener中 onOverlaysChanged 方法的还是 OverlayManagerService.java
private final class OverlayChangeListener
implements OverlayManagerServiceImpl.OverlayChangeListener {
@Override
public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
//更新Setting文件
schedulePersistSettings();
FgThread.getHandler().post(() -> {
// MIUI MOD: START
final int[] liveUserIds;
if (targetPackageName != null && targetPackageName.equals("android")) {
liveUserIds = mUserManager.getUserIds();
} else {
liveUserIds = new int[] {userId};
}
for (int targetUserId : liveUserIds) {
updateAssets(targetUserId, targetPackageName);
final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
Uri.fromParts("package", targetPackageName, null));
//只有注册了 FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 广播的才能接收到
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
try {
//发送 ACTION_OVERLAY_CHANGED 广播 ,
ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
targetUserId);
} catch (RemoteException e) {
// Intentionally left empty.
}
}
// END
});
}
}
在framework/base下面?NavigationModeController.java 注册了此广播监听
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
//当前导航栏手势
int mode = getCurrentInteractionMode(mCurrentUserContext);
if (mode == NAV_BAR_MODE_GESTURAL) {
//如果当前是手势导航执行
switchToDefaultGestureNavOverlayIfNecessary();
}
mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode)));
//回调通知onNavigationModeChanged 这里我们不关心其他应用,只需要关心 NavigationBarFragment.java
//中的回调既可以
if (notify) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onNavigationModeChanged(mode);
}
}
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java?
NavigationBarFragment.java 更新布局,重新加载布局.
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
//更新 PinningGestures 导航
updateScreenPinningGestures();
int userId = ActivityManagerWrapper.getInstance().getCurrentUserId();
if (userId != UserHandle.USER_SYSTEM) {
mHandler.post(() -> {
//重新加载fragment,更新布局文件
FragmentHostManager fragmentHost = FragmentHostManager.get(mNavigationBarView);
fragmentHost.reloadFragments();
});
}
}
|