魅族Android7-0-Settings源码分析

魅族Android7-0-Settings源码分析

前言

只能说对魅族手机系统很是无奈啊!!!为了分析魅族手机无法通过常见的方法跳到系统设置的无障碍服务,只好通过豌豆荚将settings.apk导出然后进行反编译,跟踪源码…….

准备工作(反编译apk以及AndroidManifest.xml文件)

  1. 从反编译一开始就碰上了坑,利用dex2jar-2.0无法导出classes-dex2jar.jar文件,原因暂未找到,解决方案是将classes.dex用nodepad++打开,将037,改成036,保存,再用dex2jar。

参考链接:https://www.jianshu.com/p/55bf5f688e9a

  1. 反编译AndroidManifest.xml文件可请教谷歌

Settings应用的启动类

首先跟踪Settings的启动类,查看package/app/Settings/AndroidManifest.xml文件

1
2
3
4
5
6
7
<activity-alias android:label="@string/settings_label_launcher" android:launchMode="singleTask" android:name="com.android.settings.Settings" android:targetActivity="com.android.settings.Settings" android:taskAffinity="com.android.settings">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>

第一次遇到标签,一脸懵逼o((⊙﹏⊙))o,查了才知道是作为一个已存在Activity的别名,可用来设置某个Activity的快捷入口。举例:某个Activity需要进入应用的主界面才能够点击进入,如果使用了该标签,甚至可以在桌面生成一个该Activity的图标,然后点击桌面图标直接进入该Activity。
从AndroidManifest.xml可以看到,Settings的启动类为Settings.java,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
public class Settings extends SettingsActivity
{
public static class AccessibilityDaltonizerSettingsActivity
extends SettingsActivity
{}

public static class AccessibilitySettingsActivity
extends SettingsActivity
{}

public static class AccountSettingsActivity
extends SettingsActivity
{}

public static class AccountSyncSettingsActivity
extends SettingsActivity
{}

public static class AdvancedAppsActivity
extends SettingsActivity
{}

public static class AdvancedWifiSettingsActivity
extends SettingsActivity
{}

public static class AllApplicationsActivity
extends SettingsActivity
{}

public static class AndroidBeamSettingsActivity
extends SettingsActivity
{}

public static class ApnEditorActivity
extends SettingsActivity
{}

public static class ApnSettingsActivity
extends SettingsActivity
{}

public static class AppDrawOverlaySettingsActivity
extends SettingsActivity
{}

public static class AppMemoryUsageActivity
extends SettingsActivity
{}

public static class AppNotificationSettingsActivity
extends SettingsActivity
{}

public static class AppWriteSettingsActivity
extends SettingsActivity
{}

public static class AvailableVirtualKeyboardActivity
extends SettingsActivity
{}

public static class BackgroundCheckSummaryActivity
extends SettingsActivity
{}

public static class BatterySaverSettingsActivity
extends SettingsActivity
{}

public static class BluetoothSettingsActivity
extends SettingsActivity
{}

public static class CaptioningSettingsActivity
extends SettingsActivity
{}

public static class ChooseAccountActivity
extends SettingsActivity
{}

public static class ConfigureNotificationSettingsActivity
extends SettingsActivity
{}

public static class CryptKeeperSettingsActivity
extends SettingsActivity
{}

public static class DataUsageSummaryActivity
extends SettingsActivity
{}

public static class DateTimeSettingsActivity
extends SettingsActivity
{}

public static class DevelopmentSettingsActivity
extends SettingsActivity
{}

public static class DeviceAdminSettingsActivity
extends SettingsActivity
{}

public static class DeviceInfoSettingsActivity
extends SettingsActivity
{}

public static class DeviceSettings
extends SettingsActivity
{}

public static class DisplaySettingsActivity
extends SettingsActivity
{}

public static class DomainsURLsAppListActivity
extends SettingsActivity
{}

public static class DreamSettingsActivity
extends SettingsActivity
{}

public static class FingerprintEnrollSuggestionActivity
extends FingerprintEnrollIntroduction
{}

public static class FingerprintSuggestionActivity
extends FingerprintSettings
{}

public static class HighPowerApplicationsActivity
extends SettingsActivity
{}

public static class HomeSettingsActivity
extends SettingsActivity
{}

public static class IccLockSettingsActivity
extends SettingsActivity
{}

public static class ImeiInformationActivity
extends SettingsActivity
{}

public static class InputMethodAndLanguageSettingsActivity
extends SettingsActivity
{}

public static class KeyboardLayoutPickerActivity
extends SettingsActivity
{}

public static class LocalePickerActivity
extends SettingsActivity
{}

public static class LocationSettingsActivity
extends SettingsActivity
{}

public static class ManageApplicationsActivity
extends SettingsActivity
{}

public static class ManageAssistActivity
extends SettingsActivity
{}

public static class MemorySettingsActivity
extends SettingsActivity
{}

public static class NotificationAccessSettingsActivity
extends SettingsActivity
{}

public static class NotificationAppListActivity
extends SettingsActivity
{}

public static class NotificationStationActivity
extends SettingsActivity
{}

public static class NotificationStatusbarSettingsActivity
extends SettingsActivity
{}

public static class OtherSoundSettingsActivity
extends SettingsActivity
{}

public static class OverlaySettingsActivity
extends SettingsActivity
{}

public static class PaymentSettingsActivity
extends SettingsActivity
{}

public static class PersonalSettings
extends SettingsActivity
{}

public static class PhysicalKeyboardActivity
extends SettingsActivity
{}

public static class PowerUsageSummaryActivity
extends SettingsActivity
{}

public static class PrintJobSettingsActivity
extends SettingsActivity
{}

public static class PrintSettingsActivity
extends SettingsActivity
{}

public static class PrivacySettingsActivity
extends SettingsActivity
{}

public static class PrivateVolumeForgetActivity
extends SettingsActivity
{}

public static class PrivateVolumeSettingsActivity
extends SettingsActivity
{}

public static class PublicVolumeSettingsActivity
extends SettingsActivity
{}

public static class RunningServicesActivity
extends SettingsActivity
{}

public static class SavedAccessPointsSettingsActivity
extends SettingsActivity
{}

public static class ScreenLockSuggestionActivity
extends ChooseLockGeneric
{}

public static class SecuritySettingsActivity
extends SettingsActivity
{}

public static class SimSettingsActivity
extends SettingsActivity
{}

public static class SimStatusActivity
extends SettingsActivity
{}

public static class SoundSettingsActivity
extends SettingsActivity
{}

public static class SpellCheckersSettingsActivity
extends SettingsActivity
{}

public static class StatusActivity
extends SettingsActivity
{}

public static class StorageSettingsActivity
extends SettingsActivity
{}

public static class StorageUseActivity
extends SettingsActivity
{}

public static class SystemSettings
extends SettingsActivity
{}

public static class TestingSettingsActivity
extends SettingsActivity
{}

public static class TetherSettingsActivity
extends SettingsActivity
{}

public static class TextToSpeechSettingsActivity
extends SettingsActivity
{}

public static class TopLevelSettings
extends SettingsActivity
{}

public static class TrustedCredentialsSettingsActivity
extends SettingsActivity
{}

public static class UsageAccessSettingsActivity
extends SettingsActivity
{}

public static class UserDictionarySettingsActivity
extends SettingsActivity
{}

public static class UserSettingsActivity
extends SettingsActivity
{}

public static class VpnSettingsActivity
extends SettingsActivity
{}

public static class VrListenersSettingsActivity
extends SettingsActivity
{}

public static class WallpaperSettingsActivity
extends SettingsActivity
{}

public static class WallpaperSuggestionActivity
extends SettingsActivity
{}

public static class WifiAPITestActivity
extends SettingsActivity
{}

public static class WifiCallingSettingsActivity
extends SettingsActivity
{}

public static class WifiCallingSuggestionActivity
extends SettingsActivity
{}

public static class WifiDisplaySettingsActivity
extends SettingsActivity
{}

public static class WifiInfoActivity
extends SettingsActivity
{}

public static class WifiP2pSettingsActivity
extends SettingsActivity
{}

public static class WifiSettingsActivity
extends SettingsActivity
{}

public static class WirelessSettings
extends SettingsActivity
{}

public static class WirelessSettingsActivity
extends SettingsActivity
{}

public static class WriteSettingsActivity
extends SettingsActivity
{}

public static class ZenAccessSettingsActivity
extends SettingsActivity
{}

public static class ZenModeAutomationSettingsActivity
extends SettingsActivity
{}

public static class ZenModeAutomationSuggestionActivity
extends SettingsActivity
{}

public static class ZenModeEventRuleSettingsActivity
extends SettingsActivity
{}

public static class ZenModeExternalRuleSettingsActivity
extends SettingsActivity
{}

public static class ZenModePrioritySettingsActivity
extends SettingsActivity
{}

public static class ZenModeScheduleRuleSettingsActivity
extends SettingsActivity
{}

public static class ZenModeSettingsActivity
extends SettingsActivity
{}

public static class ZenModeVisualInterruptionSettingsActivity
extends SettingsActivity
{}
}

Settings.java定义了大量静态内部类,没有任何跟界面UI相关的内容。这时候我就很困惑了,跳转到无障碍服务设置界面的内部实现是什么样的,不过从源码上看应该是AccessibilitySettingsActivity这个类,其继承自SettingsActivity类,所以继续跟踪。

SettingsActivity

1
2
3
public class SettingsActivity extends SettingsDrawerActivity
implements PreferenceManager.OnPreferenceTreeClickListener, PreferenceFragment.OnPreferenceStartFragmentCallback, ButtonBarHandler, FragmentManager.OnBackStackChangedListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener, MenuItemCompat.OnActionExpandListener
{

SettingsDrawerActivity类最终继承自Activity,所以我们应该从onCreate()入手。

1
2
3
4
5
6
7
8
9
  protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
System.currentTimeMillis();
Object localObject = Settings.Secure.getUriFor("mz_current_power_mode");
getContentResolver().registerContentObserver((Uri)localObject, false, this.mPowerSaveObserver, -1);
// Should happen before any call to getIntent()
getMetaData();
localObject = getIntent();

这里有个获取MetaData的方法,我们看一下这个方法的具体实现,同时我们从注释还可以看到这个方法需要在调用getIntent()之前进行调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void getMetaData()
{
try
{
ActivityInfo localActivityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
if (localActivityInfo != null)
{
if (localActivityInfo.metaData == null) {
return;
}
this.mFragmentClass = localActivityInfo.metaData.getString("com.android.settings.FRAGMENT_CLASS");
return;
}
}
catch (PackageManager.NameNotFoundException localNameNotFoundException)
{
Log.d("Settings", "Cannot get Metadata for: " + getComponentName().toString());
return;
}
}

这个函数的作用就是从Activity标签中获取meta-data中key为

。所以我们应该再去AndroidManifest.xml查看AccessibilitySettingsActivity相关内容,具体如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
```java
<activity android:configChanges="keyboardHidden|orientation|screenSize" android:icon="@drawable/mz_function_list_ic_accessibility" android:label="@string/accessibility_settings" android:name="com.android.settings.Settings$AccessibilitySettingsActivity" android:taskAffinity="">
<intent-filter android:priority="1">
<action android:name="android.settings.ACCESSIBILITY_SETTINGS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="com.android.settings" android:path="/accessibility_settings" android:scheme="flyme_3dtouch"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.VOICE_LAUNCH"/>
<category android:name="com.android.settings.SHORTCUT"/>
</intent-filter>
<intent-filter android:priority="3">
<action android:name="com.android.settings.action.SETTINGS"/>
</intent-filter>
<meta-data android:name="com.android.settings.category" android:value="com.android.settings.category.system"/>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS" android:value="com.meizu.settings.accessibility.FlymeAccessibilitySettings"/>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" android:value="true"/>
<meta-data android:name="usagestats.event" android:value="accessibility_settings-click"/>
</activity>

从上面可以看出mFragmentClass = “com.meizu.settings.accessibility.FlymeAccessibilitySettings”。
找到了Fragment,就要看看它是怎么加载Fragment的,继续看onCreate函数,接下来就跟踪到了getIntent()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public Intent getIntent()
{
Intent localIntent1 = super.getIntent();
Object localObject = getStartingFragmentClass(localIntent1);
if (localObject != null)
{
Intent localIntent2 = new Intent(localIntent1);
localIntent2.putExtra(":settings:show_fragment", (String)localObject);
localObject = localIntent1.getExtras();
if (localObject != null) {}
for (localObject = new Bundle((Bundle)localObject);; localObject = new Bundle())
{
((Bundle)localObject).putParcelable("intent", localIntent1);
localIntent2.putExtra(":settings:show_fragment_args", (Bundle)localObject);
return localIntent2;
}
}
return localIntent1;
}

private String getStartingFragmentClass(Intent paramIntent)
{
if (this.mFragmentClass != null) {
return this.mFragmentClass;
}
String str = paramIntent.getComponent().getClassName();
if (str.equals(getClass().getName())) {
return null;
}
if ((!"com.android.settings.ManageApplications".equals(str)) && (!"com.android.settings.RunningServices".equals(str)))
{
paramIntent = str;
if (!"com.android.settings.applications.StorageUse".equals(str)) {}
}
else
{
paramIntent = ManageApplications.class.getName();
}
return paramIntent;
}

从源码可以看出getIntent()就是返回了一个Intent,并传了一个参数(key为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
分析了getIntent,继续往下看onCreate函数,看这个intent是如何使用的。
```java
if (!this.mIsShowingDashboard)
{
this.mDisplaySearch = false;
if (this.mIsShortcut) {
this.mDisplayHomeAsUpEnabled = bool;
}
for (;;)
{
setTitleFromIntent((Intent)localObject);
switchToFragment(str1, ((Intent)localObject).getBundleExtra(":settings:show_fragment_args"), true, false, this.mInitialTitleResId, this.mInitialTitle, false);
break;
if (bool)
{
this.mDisplayHomeAsUpEnabled = true;
this.mDisplaySearch = false;
}
else
{
this.mDisplayHomeAsUpEnabled = true;
}
}
}

从上面源码大概能看出关键部分应该是switchToFragment()函数。我们再看一下switchToFragment的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private Fragment switchToFragment(String paramString, Bundle paramBundle, boolean paramBoolean1, boolean paramBoolean2, int paramInt, CharSequence paramCharSequence, boolean paramBoolean3)
{
if (FlymeDashboardSummary.isConnectivitySettingsFragment(paramString))
{
FlymeDashboardSummary.startWithFragment(this, paramString, paramBundle, null, 0, this.mInitialTitleResId, this.mInitialTitle);
finish();
return null;
}
if ((!paramBoolean1) || (isValidFragment(paramString)))
{
paramString = Fragment.instantiate(this, paramString, paramBundle);
paramBundle = getFragmentManager().beginTransaction();
paramBundle.replace(this.mMainContentId, paramString);
if (paramBoolean3) {
TransitionManager.beginDelayedTransition(this.mContent);
}
if (paramBoolean2) {
paramBundle.addToBackStack(":settings:prefs");
}
if (paramInt <= 0) {
break label145;
}
paramBundle.setBreadCrumbTitle(paramInt);
}
for (;;)
{
paramBundle.commitAllowingStateLoss();
getFragmentManager().executePendingTransactions();
return paramString;
throw new IllegalArgumentException("Invalid fragment for this activity: " + paramString);
label145:
if (paramCharSequence != null) {
paramBundle.setBreadCrumbTitle(paramCharSequence);
}
}
}

从上面可以看出先通过指定的mFragment实例化fragment,然后通过FragmentTransaction的replace方法加载fragment。
通过上述分析,我们知道了Settings.apk如何通过隐式的Intent调转到对应的Activity布局。
当然到这一步还是不能帮我解决问题,还是没有解决魅族手机无法通过常见的方法跳到系统设置的无障碍服务的问题。

FlymeAccessibilitySettings类

从上面的分析我们已经能够知道魅族手机设置–>辅助功能对应的Fragment是FlymeAccessibilitySettings这个类,源码如下:

1
2
3
4
5
6
7
8
9
10
public class FlymeAccessibilitySettings  extends SettingsPreferenceFragment 
implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, Indexable
{
....
}
public class SettingsPreferenceFragment extends PreferenceFragment
implements DialogCreatable
{
...
}

可以看出FlymeAccessibilitySettings是继承自PreferenceFragment类,它一个最重要的方法是调用addPreferencesFromResource()参数为xml资源id,来加载静态xml资源文件 (在res文件夹下新建xml文件夹,再在xml文件中新建对应的xml资源),完成Preference界面的构建。所以我们可以依次找到FlymeAccessibilitySettings对应的xml资源文件为mz_accessibility_settings.xml,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<android.preference.PreferenceScreen android:title="@string/accessibility_settings"
xmlns:android="http://schemas.android.com/apk/prv/res/android">
<android.preference.PreferenceCategory android:title="@string/interactive" android:key="interactive" />
<android.preference.Preference android:title="@string/multi_task_title" android:key="multi_task" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.FlymeMultiTaskFragment" />
<android.preference.Preference android:title="@string/quick_wakeup_title" android:key="quick_wakeup" android:summary="@string/quick_wakeup_tips" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.FlymeQuickWakeupFragment" />
<android.preference.Preference android:title="@string/smart_voice_wakeup" android:key="smart_voice_wakeup" android:summary="@string/smart_voice_wakeup_tips" android:widgetLayout="@167968832" />
<android.preference.Preference android:title="@string/smart_touch_title" android:key="smart_touch" android:summary="@string/smart_touch_tips" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.FlymeSmartTouchFragment" />
<android.preference.Preference android:title="@string/force_touch_title" android:key="force_touch" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.ForceTouchSettingsFragment" />
<android.preference.PreferenceCategory android:title="@string/header_category_lab" android:key="interactive" />
<android.preference.Preference android:title="@string/safe_family_title" android:key="safe_family" android:widgetLayout="@167968832" />
<android.preference.Preference android:title="@string/children_mode_title" android:key="children_mode" android:widgetLayout="@167968832" />
<android.preference.Preference android:title="@string/app_clone_title" android:key="app_clone" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.appclone.AppCloneSettings" />
<android.preference.Preference android:title="@string/game_mode_title" android:key="game_mode" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.GameModeSettings" />
<android.preference.Preference android:title="@string/red_envelope_title" android:key="red_envelope_assistant" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.notificationstatusbar.FlymeRedEnvelopeAssistantSettings" />
<android.preference.Preference android:title="@string/flashlamp_effects_title" android:key="flashlamp_effects" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.FlymeFlashLampEffectsSettings" />
<android.preference.Preference android:title="@string/classic_mode" android:key="classic_mode" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.accessibility.FlymeClassicModeFragment" />
<android.preference.PreferenceCategory android:title="@string/accessiblity_category_system" android:key="accessiblity_category_system" />
<ListPreference android:persistent="false" android:entries="@array/home_key_behavior_double_click_entries" android:title="@string/home_key_behavior_double_click" android:key="home_double_click" android:entryValues="@array/home_key_behavior_double_click_values" />
<ListPreference android:persistent="false" android:entries="@array/home_key_behavior_entries" android:title="@string/home_key_behavior" android:key="home_long_press" android:entryValues="@array/home_key_behavior_values" />
<ListPreference android:persistent="false" android:entries="@array/keyboard_back_behavior_entries" android:title="@string/keyboard_back_behavior" android:key="keyboard_back_behavior" android:entryValues="@array/keyboard_back_behavior_values" />
<com.meizu.common.preference.SwitchPreference android:persistent="false" android:title="@string/headset_middlekey_wakeup" android:key="headset_middlekey_wakeup" android:summary="@string/headset_middlekey_wakeup_summay" />
<com.meizu.common.preference.SwitchPreference android:title="@string/light_feedback_enable_title" android:key="light_feedback" android:defaultValue="true" />
<com.meizu.common.preference.SwitchPreference android:title="@string/keyguard_palm_rejection_title" android:key="palm_rejection" android:summary="@string/keyguard_palm_rejection_summary" android:defaultValue="true" />
<com.meizu.common.preference.SwitchPreference android:title="@string/hall_switch_title" android:key="use_cover" android:summary="@string/hall_switch_summary" android:defaultValue="true" />
<com.meizu.common.preference.SwitchPreference android:title="@string/mcharge_title" android:key="fast_charge" android:defaultValue="true" />
<android.preference.Preference android:title="@string/search_settings_title" android:key="search_settings" android:widgetLayout="@167968832" />
<android.preference.Preference android:title="@string/scheduled_power_onandoff_title" android:key="scheduled_power" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.scheduledpower.ScheduledPowerFragment" />
<android.preference.Preference android:title="@string/drive_mode_title" android:key="drive_mode" android:widgetLayout="@167968832" android:fragment="com.meizu.settings.drivemode.MzDriveModeFragment" />
<android.preference.PreferenceScreen android:title="@string/accessible" android:key="accessible_settings" android:fragment="com.android.settings.accessibility.AccessibilitySettings" />
<com.meizu.settings.calibration.ProximityCalibraton android:title="@string/sensor_adjust_title" android:key="sensor_adjust_proximity" />
<com.meizu.settings.calibration.GsensorCalibration android:title="@string/gsensor_calibrate_text" android:key="sensor_adjust_gsensor" />
<android.preference.Preference android:title="@string/development_settings_title" android:key="development_settings" android:widgetLayout="@167968832" android:fragment="com.android.settings.DevelopmentSettings" />
<com.meizu.common.preference.SwitchPreference android:title="@string/data_collection_title" android:key="data_collection" />
</android.preference.PreferenceScreen>

对照着手机界面,我们可以确认这个xml文件就是FlymeAccessibilitySettings类的静态资源文件,从中我们可以找到魅族手机无障碍服务对应的Fragment:

1
<android.preference.PreferenceScreen android:title="@string/accessible" android:key="accessible_settings" android:fragment="com.android.settings.accessibility.AccessibilitySettings" />

在PreferenceFragment中点击列表项的点击监听方法为onPreferenceTreeClick();在FlymeAccessibilitySettings类中的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
public boolean onPreferenceTreeClick(PreferenceScreen paramPreferenceScreen, Preference paramPreference)
{
int k = 0;
int m = 0;
int i = 0;
int j = 1;
this.mUsageStatsProxy.reportData("AccessibilitySettings", paramPreference.getKey(), paramPreference);
if (paramPreference == this.mHomeLightSwitchPreference)
{
paramPreferenceScreen = this.mContentResolver;
if (this.mHomeLightSwitchPreference.isChecked()) {
i = 1;
}
Settings.System.putInt(paramPreferenceScreen, "light_feedback_enabled", i);
return true;
}
if (paramPreference == this.mDataCollectionSwitch)
{
if (this.mDataCollectionSwitch.isChecked())
{
warnDataCollection();
return true;
}
Settings.System.putInt(this.mContentResolver, "meizu_data_collection", 0);
return true;
}
if (paramPreference == this.mSearchPreference)
{
paramPreferenceScreen = new Intent();
paramPreferenceScreen.setAction("com.meizu.net.search.setting");
paramPreferenceScreen.addFlags(335544320);
try
{
startActivity(paramPreferenceScreen);
return true;
}
catch (ActivityNotFoundException paramPreferenceScreen)
{
Log.e("AccessibilitySettings", "ActivityNotFoundException for search preference");
return true;
}
}
if (paramPreference == this.mHeadsetMiddleKeyWakeupPreference)
{
paramPreferenceScreen = this.mContentResolver;
i = k;
if (this.mHeadsetMiddleKeyWakeupPreference.isChecked()) {
i = 1;
}
Settings.System.putInt(paramPreferenceScreen, "headset_middle_key_wakeup", i);
return true;
}
if (paramPreference == this.mSmartVoiceWakeupPreference) {
try
{
paramPreferenceScreen = new Intent("com.mediatek.voicecommand.VOICE_CONTROL_SETTINGS");
paramPreferenceScreen.addFlags(335544320);
startActivity(paramPreferenceScreen);
return true;
}
catch (ActivityNotFoundException paramPreferenceScreen)
{
Log.e("AccessibilitySettings", "ActivityNotFoundException for MTK voice wakeup preference");
startFragment(this, "com.meizu.settings.accessibility.FlymeSmartVoiceWakeupFragment", 2131627644, -1, null);
return true;
}
}
if (this.mFlymeInnovation.onPreferenceTreeClick(paramPreferenceScreen, paramPreference)) {
return true;
}
if (paramPreference == this.mPalmRejection)
{
paramPreferenceScreen = this.mContentResolver;
i = m;
if (this.mPalmRejection.isChecked()) {
i = 1;
}
Settings.System.putInt(paramPreferenceScreen, "keyguard_palm_rejection", i);
return true;
}
if (paramPreference == this.mFastCharge) {
if (!this.mFastCharge.isChecked())
{
this.mChargeDialog = new AlertDialog.Builder(this.mContext, 5).setMessage(2131628228).setPositiveButton(17039379, this).setNegativeButton(17039369, this).show();
this.mChargeDialog.setOnDismissListener(new DialogInterface.OnDismissListener()
{
public void onDismiss(DialogInterface paramAnonymousDialogInterface)
{
boolean bool = true;
paramAnonymousDialogInterface = FlymeAccessibilitySettings.-get4(FlymeAccessibilitySettings.this);
if (Settings.Global.getInt(FlymeAccessibilitySettings.-get0(FlymeAccessibilitySettings.this), "mz_fast_charge", 1) == 1) {}
for (;;)
{
paramAnonymousDialogInterface.setChecked(bool);
return;
bool = false;
}
}
});
}
}
for (;;)
{
return super.onPreferenceTreeClick(paramPreferenceScreen, paramPreference);
Settings.Global.putInt(this.mContentResolver, "mz_fast_charge", 1);
continue;
if (paramPreference == this.mHallSwitch)
{
boolean bool = this.mHallSwitch.isChecked();
ContentResolver localContentResolver = this.mContentResolver;
if (bool) {}
for (i = j;; i = 0)
{
Settings.System.putInt(localContentResolver, "hall_switch", i);
this.mUsageStatsProxy.reportData(FlymeAccessibilitySettings.class.getSimpleName(), paramPreference.getKey(), paramPreference);
break;
}
}
if (paramPreference == this.mSafeFamilyPreference) {
startActivity(getBreakingScamIntent());
}
}
}

从上面我们可以分析出对辅助功能界面各个item的处理,不过最重要的对无障碍的处理是

paramPreference)```,继续跟踪下去,FlymeAccessibilitySettings的父类为SettingsPreferenceFragment,源码如下:
1
2
3
4
5
6
7
8
```java
public boolean onPreferenceTreeClick(PreferenceScreen paramPreferenceScreen, Preference paramPreference)
{
if (paramPreference.getFragment() != null) {
return ((SettingsActivity)getActivity()).onPreferenceStartFragment(this, paramPreference);
}
return super.onPreferenceTreeClick(paramPreferenceScreen, paramPreference);
}

因为无障碍服务对应的是另一个Fragment,所以进入if块,调用onPreferenceStartFragment(),源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public boolean onPreferenceStartFragment(SettingsPreferenceFragment paramSettingsPreferenceFragment, android.preference.Preference paramPreference)
{
paramSettingsPreferenceFragment = paramPreference.getTitle();
if (paramPreference.getFragment().equals(WallpaperTypeSettings.class.getName())) {
paramSettingsPreferenceFragment = getString(2131625303);
}
startPreferencePanel(paramPreference.getFragment(), paramPreference.getExtras(), -1, paramSettingsPreferenceFragment, null, 0);
return true;
}

public void startPreferencePanel(String paramString, Bundle paramBundle, int paramInt1, CharSequence paramCharSequence, Fragment paramFragment, int paramInt2)
{
String str = null;
if (paramInt1 < 0) {
if (paramCharSequence == null) {
break label39;
}
}
label39:
for (str = paramCharSequence.toString();; str = "")
{
Utils.startWithFragment(this, paramString, paramBundle, paramFragment, paramInt2, paramInt1, str, this.mIsShortcut);
return;
}
}

到这一步可以发现最终调用的是

too young too native
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
继续往下看:
```java
public static void startWithFragment(Context paramContext, String paramString, Bundle paramBundle, Fragment paramFragment, int paramInt1, int paramInt2, CharSequence paramCharSequence, boolean paramBoolean)
{
paramString = onBuildStartFragmentIntent(paramContext, paramString, paramBundle, null, paramInt2, paramCharSequence, paramBoolean);
if (paramFragment == null)
{
paramContext.startActivity(paramString);
return;
}
paramFragment.startActivityForResult(paramString, paramInt1);
}

public static Intent onBuildStartFragmentIntent(Context paramContext, String paramString1, Bundle paramBundle, String paramString2, int paramInt, CharSequence paramCharSequence, boolean paramBoolean)
{
Intent localIntent = new Intent("android.intent.action.MAIN");
localIntent.setClass(paramContext, SubSettings.class);
localIntent.putExtra(":settings:show_fragment", paramString1);
localIntent.putExtra(":settings:show_fragment_args", paramBundle);
localIntent.putExtra(":settings:show_fragment_title_res_package_name", paramString2);
localIntent.putExtra(":settings:show_fragment_title_resid", paramInt);
localIntent.putExtra(":settings:show_fragment_title", paramCharSequence);
localIntent.putExtra(":settings:show_fragment_as_shortcut", paramBoolean);
return localIntent;
}

不难发现从调用无障碍服务的方式是startActivityForResult(),在这个之前会先调用onBuildStartFragmentInten()构造一个intent。首先我们看到方法内部所调用的Activity为SubSettings类,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SubSettings extends SettingsActivity
{
protected boolean isValidFragment(String paramString)
{
Log.d("SubSettings", "Launching fragment " + paramString);
return true;
}

public boolean onNavigateUp()
{
finish();
return true;
}
}

SubSettings继承自SettingsActivity,我们在之前的分析已经能得出SettingsActivity加载Fragment的方式是从AndroidManifest.xml的Activity标签中获取meta-data中key为

1
2
3
4
5
6
7
```java
<activity android:configChanges="keyboardHidden|orientation|screenSize" android:exported="true" android:name="com.android.settings.SubSettings" android:parentActivityName="com.android.settings.Settings" android:permission="com.meizu.permission.SEARCH_SETTINGS" android:screenOrientation="portrait" android:taskAffinity="com.android.settings">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
</activity>

并没有我们所需要的值,但在getIntent中我们可以发现intent值来自

1
2
3
4
5
6
代码如下:
```java
intent.setAction("android.intent.action.SEARCH");
intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.SubSettings"));
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, "com.android.settings.accessibility.AccessibilitySettings");
startActivity(intent);

因为跳转SubSettings这个Activity需要声明一个permission,所以我们必须在我们应用加上这个权限声明,但是这会引来另一个问题:Failure [INSTALL_FAILED_DUPLICATE_PERMISSION,这个错误的意思是正要安装的APP的自定义权限与手机上已有APP的自定义权限名字相同,但两个APP具有不同的签名信息导致安装失败。