ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Android16 原生设置部分问题修改

Android16 原生设置部分问题修改 一、关闭媒体音量睡眠模式中关闭媒体音量但是在我们自己的设置中依然可以调节音量但是调节后会自动恢复静音。先看下原生设置的调用逻辑。//Settings/src/com/android/settings/notification/modes/ZenModeOtherPreferenceController.java Override public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean allow (Boolean) newValue; return savePolicy(policy - policy.allowCategory(getCategory(), allow)); } private int getCategory() { switch (getPreferenceKey()) { case modes_category_alarm: return PRIORITY_CATEGORY_ALARMS; case modes_category_media: return PRIORITY_CATEGORY_MEDIA; case modes_category_system: return PRIORITY_CATEGORY_SYSTEM; case modes_category_reminders: return PRIORITY_CATEGORY_REMINDERS; case modes_category_events: return PRIORITY_CATEGORY_EVENTS; } return -1; }ZenModeOtherPreferenceController继承了AbstractZenModePreferenceController。//Settings/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java /** * Subclasses should call this method (or a more specific one, like {link #savePolicy} from * their {code onPreferenceChange()} or similar, in order to apply changes to the mode being * edited (e.g. {code saveMode(mode - { mode.setX(value); return mode; } }. * * param updater Function to update the {link ZenMode}. Modifying and returning the same * instance is ok. */ protected final boolean saveMode(FunctionZenMode, ZenMode updater) { checkState(mBackend ! null); ZenMode mode mZenMode; if (mode null) { Log.wtf(TAG, Cannot save mode, it hasnt been loaded ( getClass() )); return false; } mode updater.apply(mode); mBackend.updateMode(mode); return true; } protected final boolean savePolicy(FunctionZenPolicy.Builder, ZenPolicy.Builder updater) { return saveMode(mode - { ZenPolicy.Builder policyBuilder new ZenPolicy.Builder(mode.getPolicy()); policyBuilder updater.apply(policyBuilder); mode.setPolicy(policyBuilder.build()); return mode; }); }最终调用到了mBackend.updateMode。//frameworks/base/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java public void updateMode(ZenMode mode) { if (mode.isManualDnd()) { try { NotificationManager.Policy dndPolicy new ZenModeConfig().toNotificationPolicy(mode.getPolicy()); mNotificationManager.setNotificationPolicy(dndPolicy, /* fromUser */ true); mNotificationManager.setManualZenRuleDeviceEffects( mode.getRule().getDeviceEffects()); } catch (Exception e) { Log.w(TAG, Error updating manual mode, e); } } else { mNotificationManager.updateAutomaticZenRule(mode.getId(), mode.getRule(), /* fromUser */ true); } }可以看到是在NotificationManager.setNotificationPolicy这样的话我们就可以在自己的应用里面获取到对应的设置了。private NotificationManager.Policy mPolicy; private final NotificationManager mNotificationManager; mNotificationManager (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE); public boolean isPriorityCategoryEnabled(int categoryType) { updatePolicy(); return (mPolicy.priorityCategories categoryType) ! 0; } public void updatePolicy() { if (mNotificationManager ! null) { mPolicy mNotificationManager.getNotificationPolicy(); } }在我们需要刷新视图的方法中添加isPriorityCategoryEnabled()就可以控制用户能否调节音量了。二、修改默认的拼写检查工具系统-键盘-拼写检查工具默认是AOSP拼写检查工具我们系统内置了Gboard因此希望默认是Gboard的拼写检查工具。先看下设置中的数据是从哪获取的。//Settings/src/com/android/settings/inputmethod/SpellCheckersSettings.java Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.spellchecker_prefs); mSpellCheckerLanaguagePref findPreference(KEY_SPELL_CHECKER_LANGUAGE); mTsm (TextServicesManager) getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); mCurrentSci mTsm.getCurrentSpellChecker(); mEnabledScis mTsm.getEnabledSpellCheckers(); populatePreferenceScreen(); }//frameworks/base/services/core/java/com/android/server/textservices/TextServicesManagerService.java private static class TextServicesData { ... NonNull private String getSelectedSpellChecker() { return getString(Settings.Secure.SELECTED_SPELL_CHECKER, ); } Nullable public SpellCheckerInfo getCurrentSpellChecker() { final String curSpellCheckerId getSelectedSpellChecker(); if (TextUtils.isEmpty(curSpellCheckerId)) { return null; } return mSpellCheckerMap.get(curSpellCheckerId); } ... }由于SELECTED_SPELL_CHECKER没有配置默认值service中返回的是null但实际上进入系统还是选中了AOSP的选项的那应该是有初始化配置加载了默认选项。//frameworks/base/services/core/java/com/android/server/textservices/TextServicesManagerService.java GuardedBy(mLock) private void initializeInternalStateLocked(UserIdInt int userId) { TextServicesData tsd mUserData.get(userId); if (tsd null) { tsd new TextServicesData(userId, mContext); mUserData.put(userId, tsd); } tsd.initializeTextServicesData(); SpellCheckerInfo sci tsd.getCurrentSpellChecker(); if (sci null) { sci findAvailSystemSpellCheckerLocked(null, tsd); // Set the current spell checker if there is one or more system spell checkers // available. In this case, sci is the first one in the available spell // checkers. setCurrentSpellCheckerLocked(sci, tsd); } } private SpellCheckerInfo findAvailSystemSpellCheckerLocked(String prefPackage, TextServicesData tsd) { // Filter the spell checker list to remove spell checker services that are not pre-installed ArrayListSpellCheckerInfo spellCheckerList new ArrayList(); for (SpellCheckerInfo sci : tsd.mSpellCheckerList) { if ((sci.getServiceInfo().applicationInfo.flags ApplicationInfo.FLAG_SYSTEM) ! 0) { spellCheckerList.add(sci); } } final int spellCheckersCount spellCheckerList.size(); if (spellCheckersCount 0) { Slog.w(TAG, no available spell checker services found); return null; } if (prefPackage ! null) { for (int i 0; i spellCheckersCount; i) { final SpellCheckerInfo sci spellCheckerList.get(i); if (prefPackage.equals(sci.getPackageName())) { if (DBG) { Slog.d(TAG, findAvailSystemSpellCheckerLocked: sci.getPackageName()); } return sci; } } } // Look up a spell checker based on the system locale. // TODO: Still there is a room to improve in the following logic: e.g., check if the package // is pre-installed or not. final Locale systemLocal mContext.getResources().getConfiguration().locale; final ArrayListLocale suitableLocales LocaleUtils.getSuitableLocalesForSpellChecker(systemLocal); if (DBG) { Slog.w(TAG, findAvailSystemSpellCheckerLocked suitableLocales Arrays.toString(suitableLocales.toArray(new Locale[suitableLocales.size()]))); } final int localeCount suitableLocales.size(); for (int localeIndex 0; localeIndex localeCount; localeIndex) { final Locale locale suitableLocales.get(localeIndex); for (int spellCheckersIndex 0; spellCheckersIndex spellCheckersCount; spellCheckersIndex) { final SpellCheckerInfo info spellCheckerList.get(spellCheckersIndex); final int subtypeCount info.getSubtypeCount(); for (int subtypeIndex 0; subtypeIndex subtypeCount; subtypeIndex) { final SpellCheckerSubtype subtype info.getSubtypeAt(subtypeIndex); final Locale subtypeLocale SubtypeLocaleUtils.constructLocaleFromString( subtype.getLocale()); if (locale.equals(subtypeLocale)) { // TODO: We may have more spell checkers that fall into this category. // Ideally we should pick up the most suitable one instead of simply // returning the first found one. return info; } } } } if (spellCheckersCount 1) { Slog.w(TAG, more than one spell checker service found, picking first); } return spellCheckerList.get(0); }在初始化方法中看到当返回null是会去查找可用的拼写检查工具并返回列表中的第一个值。现象分析完毕可以开始进行修改了要么在查找可用拼写工具时设置优先排序将Gboard放在第一个要么配置默认选中值SELECTED_SPELL_CHECKER。这里为了方便我直接配置了默认值。//frameworks/base/packages/SettingsProvider/res/values/defaults.xml string namedef_spell_checker translatablefalse com.google.android.inputmethod.latin/com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService /string//frameworks/base/packages/SettingsProvider/src/com/android/provider/settings/DatabaseHelper.java private void loadSecureSettings(SQLiteDatabase db) { ... loadStringSetting(stmt, Settings.Secure.SELECTED_SPELL_CHECKER, R.string.def_spell_checker); ... }三、隐藏应用列表中的部分应用有些我们自己的系统应用不想让用户看到需要隐藏。原生设置中有两个区域显示应用一个是最近使用的应用一个是全部应用都需要进行隐藏。最近使用应用中已经有一个隐藏的逻辑了直接在对应位置添加应用包名即可。// packages/apps/Settings/src/com/android/settings/applications/RecentAppStatsMixin.java public class RecentAppStatsMixin implements LifecycleObserver, OnStart { private static final String TAG RecentAppStatsMixin; private static final SetString SKIP_SYSTEM_PACKAGES new ArraySet(); VisibleForTesting ListUsageStatsWrapper mRecentApps; private final int mMaximumApps; private final Context mContext; private final PackageManager mPm; private final UserManager mUserManager; private final PowerManager mPowerManager; private final ApplicationsState mApplicationsState; private final ListRecentAppStatsListener mAppStatsListeners; private Calendar mCalendar; static { SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList( android, com.android.phone, SETTINGS_PACKAGE_NAME, SYSTEMUI_PACKAGE_NAME, com.android.providers.calendar, com.android.providers.media, com.your.package.name )); }所有应用显示的逻辑中有一个filter在里面叠加一个我们的包名过滤就可以实现隐藏了。//packages/apps/Settings/src/com/android/settings/spa/app/AllAppList.kt val hiddenPackages setOf( com.your.package.name, ... com.your.package.name2 ) override fun filter( userIdFlow: FlowInt, option: Int, recordListFlow: FlowListAppRecordWithSize, ): FlowListAppRecordWithSize recordListFlow.filterItem { record - // 原有筛选条件 val baseMatch when (SpinnerItem.entries.getOrNull(option)) { SpinnerItem.Enabled - record.app.enabled !record.app.isInstantApp SpinnerItem.Disabled - isDisabled(record) SpinnerItem.Instant - isInstant(record) else - true } // 叠加包名过滤 val packageMatch record.app.packageName !in hiddenPackages baseMatch packageMatch }四、配置默认浏览器在frameworks/base/core/res/res/values/config.xml中可以配置默认浏览器在对应标签中填入想要默认的浏览器包名即可。string namedefault_browser translatablefalsecom.android.chrome/string五、Launcher3中长按应用图标时图标会消失松手后恢复显示这个问题比较奇怪逻辑上都是原生的不知道为什么要让图标消失但是在最新的android16Launcher3上又没有这个问题现象了。这里不分析具体的流程了可能最新上的又有变化感兴趣的可以自己去看下直接贴上修改代码。//packages/apps/Launcher3/src/com/android/launcher3/popup/PopupContainerWithArrow.java /** * Determines when the deferred drag should be started. * * Current behavior: * - Start the drag if the touch passes a certain distance from the original touch down. */ public DragOptions.PreDragCondition createPreDragCondition(boolean updateIconUi) { return new DragOptions.PreDragCondition() { Override public boolean shouldStartDrag(double distanceDragged) { return distanceDragged mStartDragThreshold; } Override public void onPreDragStart(DropTarget.DragObject dragObject) { if (!updateIconUi) { return; } // 这里注释掉控制显示的逻辑 if (mIsAboveIcon) { // Hide only the icon, keep the text visible. //mOriginalIcon.setIconVisible(false); //mOriginalIcon.setVisibility(VISIBLE); } else { // Hide both the icon and text. //mOriginalIcon.setVisibility(INVISIBLE); } } Override public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) { if (!updateIconUi) { return; } mOriginalIcon.setIconVisible(true); if (dragStarted) { // Make sure we keep the original icon hidden while it is being dragged. mOriginalIcon.setVisibility(INVISIBLE); } else { // TODO: add WW logging if want to add logging for long press on popup // container. // mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon); if (!mIsAboveIcon) { // Show the icon but keep the text hidden. mOriginalIcon.setVisibility(VISIBLE); mOriginalIcon.setTextVisibility(false); } } } }; }//packages/apps/Launcher3/src/com/android/launcher3/Workspace.java public void startDrag(CellInfo cellInfo, DragOptions options) { View child cellInfo.cell; mDragInfo cellInfo; // 判断是应用图标时不再进行隐藏 if (!(child instanceof BubbleTextView)) { child.setVisibility(INVISIBLE); } if (options.isAccessibleDrag) { mAccessibilityDragListener new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) { Override protected void enableAccessibleDrag(boolean enable, Nullable DragObject dragObject) { super.enableAccessibleDrag(enable, dragObject); setEnableForLayout(mLauncher.getHotseat(), enable); if (enable dragObject ! null dragObject.dragInfo instanceof LauncherAppWidgetInfo) { mLauncher.getHotseat().setImportantForAccessibility( IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); } } }; } beginDragShared(child, this, options); }
返回列表