魅族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文件

       <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>

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

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

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

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

  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()之前进行调用。

  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为com.android.settings.FRAGMENT_CLASS的值,并将其赋值给mFragmentClass 。所以我们应该再去AndroidManifest.xml查看AccessibilitySettingsActivity相关内容,具体如下:

        <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()方法:

  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为:settings:show_fragment,value为mFragmentClass指定的Fragment类名)。
分析了getIntent,继续往下看onCreate函数,看这个intent是如何使用的。

      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的实现:

  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这个类,源码如下:

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,内容如下:

<?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:

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

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

 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的处理,不过最重要的对无障碍的处理是super.onPreferenceTreeClick(paramPreferenceScreen, paramPreference),继续跟踪下去,FlymeAccessibilitySettings的父类为SettingsPreferenceFragment,源码如下:

  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(),源码如下:

  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;
    }
  }

到这一步可以发现最终调用的是Utils.startWithFragment(),我本以为见到曙光了...不过 too young too native
继续往下看:

  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类,源码如下:

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为com.android.settings.FRAGMENT_CLASS的值,我们看下SubSettings的声明:

        <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值来自super.getIntent();,最后通过调用switchToFragment()加载无障碍服务对应的Fragment。所以我们可以尝试着构造一样的intent在我们自己的应用中调用魅族手机的无障碍服务
代码如下:

        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具有不同的签名信息导致安装失败。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容