简单记录下,本次修改为网络设置中添加一个以太网的选项开关。效果如下:
与Android4.4相差较大,主要修改文件为,在以下路径 packages/apps/Settings
- res/values-zh-rCN/strings.xml
- res/values/config.xml
- res/xml/network_and_internet_v2.xml
- src/com/android/settings/network/NetworkDashboardFragment.java
- src/com/android/settings/network/EthernetPreferenceController.java(新建)
- src/com/android/settings/utils/ReflectUtil.java(新建)
接下来按照我修改的逻辑进行说明:
1、修改布局文件
路径:res/xml/network_and_internet_v2.xml
说明:其中 order 绝对值越大越靠上,有些我这里未给出的资源可复制其他选项的资源,或者私信或评论我发送你;修改完以下部分你就会发现已经有这个选项了。
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="network_and_internet_screen"
android:title="@string/network_dashboard_title"
settings:initialExpandedChildrenCount="6"> //原本这里是5
...
<!--lichang add ethernet-->
<com.android.settingslib.RestrictedSwitchPreference
settings:controller="com.android.settings.network.EthernetPreferenceController"
android:key="usb_ethernet"
android:persistent="true"
android:icon="@drawable/ic_setting_eth"
android:title="@string/settings_eth_tittle"
android:summary="@string/settings_eth_summary"
android:order="-25" />
...
2、资源文件
路径:res/values-zh-rCN/strings.xml
说明:这里主要是选项标题的一些中文显示,其实我也在 res/values/strings.xml 路径下修改了,但是感觉没啥必要在写一遍。我是直接在最后添加的。
...
<!-- lichang add ethernet -->
<string name="settings_eth_tittle">以太网</string>
<string name="settings_eth_summary">关闭</string>
<string name="settings_eth_open">开启</string>
<string name="settings_eth_close">关闭</string>
</resources>
路径:res/values/config.xml
说明:配置文件,配置功能开关;由于本次是为了添加以太网的开关选项,因此在此处添加是否显示该选项的开关。
<!-- Whether toggle_ethernet is available or not. -->
<bool name="config_show_toggle_ethernet">true</bool>
3、实现开关对应的功能
路径:src/com/android/settings/network/NetworkDashboardFragment.java
说明:简单阅读代码可以发现设置页面的功能实现都是由 preference + controller 实现的,而当前文件则是网络页面 controller 的加载或者说实例化(我这么理解应该没错吧~)。
@Override
public void onAttach(Context context) {
super.onAttach(context);
/*UNISOC: Modify for bug1145683, We do not want to display an advanced button if
* linking to this screen from condition @{*/
Bundle args = getArguments();
if (args != null) {
if (!args.getBoolean(ARG_SHOW_EXPAND_BUTTON, true)) {
mChildrenCount = Integer.MAX_VALUE;
}
}
if (FeatureFlagPersistent.isEnabled(context, FeatureFlags.NETWORK_INTERNET_V2)) {
// UNISOC:Improve the function of turning on and off the Sub
use(MultiNetworkHeaderController.class).init(getSettingsLifecycle(),
getFragmentManager(), mChildrenCount);
}
/* @} */
use(AirplaneModePreferenceController.class).setFragment(this);
use(EthernetPreferenceController.class).setFragment(this); //新加
}
路径:src/com/android/settings/network/EthernetPreferenceController.java
说明:最终的实现应该都是在 controller 里实现的,由于本次添加的是开关,于是查看了下飞行模式的 controller 并进行了部分修改。开关以太网卡的方式为修改节点,但是搁主线程里看起来非常卡顿,因此使用了线程池,虽然看起来实际开关慢了一点,但不会出现卡顿的现象。这部分代码可直接删除,然后打印 log 即可。
然后说一下这个 preference 的保存方式,修改节点的方法并不能持久保存,在关机或重启之后,会恢复为默认值,所以加了一个开机监听,在开机后将判断条件修改为关。
最后说一下保存数据的方式,说真的,我不太理解,在退出页面后数据就被重置了,不得已改成 SystemProperties 读写数据。方法贴在后面。
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network;
import android.annotation.SuppressLint;
import com.android.settings.utils.ThreadPool;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.utils.ReflectUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class EthernetPreferenceController extends TogglePreferenceController {
private static final String TAG = "EthernetPreferenceController";
private SwitchPreference mEthernetPreference;
private Fragment mFragment;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private Context mContext;
public EthernetPreferenceController(Context context, String key) {
super(context, key);
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
mContext = context;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
android.util.Log.d(TAG, "handlePreferenceTreeClick: " + preference.getKey());
if (TextUtils.equals(preference.getKey(), "usb_ethernet")) {
changeState(!isChecked());
return true;
}
return false;
}
public void setFragment(Fragment hostFragment) {
mFragment = hostFragment;
}
@Override
@AvailabilityStatus
public int getAvailabilityStatus() {
return isAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
public static boolean isAvailable(Context context) {
return context.getResources().getBoolean(R.bool.config_show_toggle_ethernet)
&& !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
mEthernetPreference = screen.findPreference(getPreferenceKey());
android.util.Log.d(TAG, "displayPreference: " + isChecked());
if (isChecked())
mEthernetPreference.setSummary(mContext.getString(R.string.settings_eth_open));
else
mEthernetPreference.setSummary(mContext.getString(R.string.settings_eth_close));
mEthernetPreference.setChecked(isChecked());
}
}
@Override
public boolean setChecked(boolean isChecked) {
return false;
}
public void changeState(boolean state) {
android.util.Log.d(TAG, "changeState to be = " + state);
if (state) {
ReflectUtil.setProperty("persist.flyscale.ethernet", "1");
mEthernetPreference.setSummary(mContext.getString(R.string.settings_eth_open));
mEthernetPreference.setChecked(true);
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
write("onnet");
}
});
} else {
ReflectUtil.setProperty("persist.flyscale.ethernet", "0");
mEthernetPreference.setSummary(mContext.getString(R.string.settings_eth_close));
mEthernetPreference.setChecked(false);
ThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
write("offnet");
}
});
}
android.util.Log.d(TAG, "changeState: currentState is = " + isChecked());
}
@Override
public boolean isChecked() {
return TextUtils.equals(ReflectUtil.getProperty("persist.flyscale.ethernet", "0"), "1");
}
//mode{onotg,offotg,onnet,offnet}
private static void write(String mode){
//这里根据需要自己写实现的功能吧
}
}
package com.android.settings.utils;
import java.lang.reflect.Method;
public final class ReflectUtil {
public static String getProperty(String key, String defaultValue) {
String value = defaultValue;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class, String.class);
value = (String)(get.invoke(c, key, defaultValue));
} catch (Exception e) {
e.printStackTrace();
}finally {
return value;
}
}
public static void setProperty(String key, String value) {
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method set = c.getMethod("set", String.class, String.class);
set.invoke(c, key, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
总之,抛开我不太理解的地方,还是相对容易的。