├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── xposed_init │ ├── java │ └── com │ │ └── qianbajin │ │ └── sportaccelerator │ │ ├── Constant.java │ │ ├── SportHook.java │ │ ├── bean │ │ └── AliStepRecord.java │ │ ├── fragment │ │ ├── AliFragment.java │ │ ├── AppListFragment.java │ │ ├── BasePreferenceFragment.java │ │ ├── QQFragment.java │ │ └── SettingFragment.java │ │ ├── ui │ │ ├── ConfigActivity.java │ │ └── MainActivity.java │ │ └── v4 │ │ ├── PreferenceFragment.java │ │ └── PreferenceManagerCompat.java │ └── res │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_config.xml │ ├── activity_main.xml │ └── preference_list_fragment.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-xxxhdpi │ └── sport.png │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── app_list.xml │ ├── config_alipay.xml │ ├── config_qq.xml │ └── setting_preference.xml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SportAccelerator 2 | 这是一个可以直接修改应用运动步数和通过修改传感器数据来达到修改运动数据的模块 3 | 酷安下载地址 https://www.coolapk.com/apk/com.qianbajin.sportaccelerator 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | defaultConfig { 7 | applicationId "com.qianbajin.sportaccelerator" 8 | minSdkVersion rootProject.ext.minSdkVersion 9 | targetSdkVersion rootProject.ext.targetSdkVersion 10 | versionCode rootProject.ext.versionCode 11 | versionName rootProject.ext.versionName 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | productFlavors{ 21 | 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | compile fileTree(include: ['*.jar'], dir: 'libs') 31 | compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" 32 | compile "com.android.support:support-v4:${rootProject.ext.supportLibVersion}" 33 | compile "com.android.support:design:${rootProject.ext.supportLibVersion}" 34 | compile 'com.alibaba:fastjson:1.1.63.android' 35 | provided 'de.robv.android.xposed:api:82' 36 | } 37 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 17 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.qianbajin.sportaccelerator.SportHook -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/Constant.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator; 2 | 3 | /** 4 | * @author Administrator 5 | * @Created at 2017/11/26 0026 17:23 6 | * @des 7 | */ 8 | 9 | public class Constant { 10 | 11 | public static final String[] SUPPORT_APP_PK_LIST = { 12 | "com.eg.android.AlipayGphone", 13 | "com.tencent.mobileqq" 14 | }; 15 | 16 | public static final String PKG_ALIPAY = "com.eg.android.AlipayGphone"; 17 | public static final String ALI_EXT = "com.eg.android.AlipayGphone:ext"; 18 | public static final String PKG_QQ = "com.tencent.mobileqq"; 19 | public static final String QQ_MSF = "com.tencent.mobileqq:MSF"; 20 | public static final String S_HEALTH = "com.sec.android.app.shealth"; 21 | public static final String SP_KEY_ALI_UPPER_LIMIT = "sp_key_ali_upper_limit"; 22 | public static final String SP_KEY_QQ_UPPER_LIMIT = "sp_key_qq_upper_limit"; 23 | public static final String SP_KEY_ALI_RATE = "sp_key_ali_rate"; 24 | public static final String SP_KEY_ALI_GAIN_STEP = "sp_key_ali_gain_step"; 25 | public static final String ALI_SP_KEY_BASESTEP = "baseStep"; 26 | public static final String ALI_SP_KEY_STEPRECORD = "stepRecord"; 27 | public static final String ALI_SP_KEY_LAST_STEP_TODAY = "last_stepinfo_today"; 28 | public static final String ALI_SP_KEY_FIRST_STEP = "firstStep"; 29 | public static final String SP_KEY_QQ_RATE = "sp_key_qq_rate"; 30 | public static final String SP_KEY_SENSOR_LOG = "sp_key_sensor_log"; 31 | public static final String SP_KEY_CONFIG_LOG = "sp_key_config_log"; 32 | public static final String SP_KEY_ALI_SENSOR_HOOK = "sp_key_ali_sensor_hook"; 33 | public static final String SP_KEY_ALI_EDIT = "sp_key_ali_edit"; 34 | public static int ALI_STEP_TOTAL; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/SportHook.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.os.Process; 7 | import android.text.TextUtils; 8 | 9 | import com.alibaba.fastjson.JSON; 10 | import com.qianbajin.sportaccelerator.bean.AliStepRecord; 11 | 12 | import java.lang.reflect.Method; 13 | import java.text.SimpleDateFormat; 14 | import java.util.Calendar; 15 | import java.util.Date; 16 | import java.util.List; 17 | import java.util.Locale; 18 | import java.util.Map; 19 | import java.util.Set; 20 | 21 | import de.robv.android.xposed.IXposedHookLoadPackage; 22 | import de.robv.android.xposed.IXposedHookZygoteInit; 23 | import de.robv.android.xposed.XC_MethodHook; 24 | import de.robv.android.xposed.XSharedPreferences; 25 | import de.robv.android.xposed.XposedBridge; 26 | import de.robv.android.xposed.XposedHelpers; 27 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 28 | /** 29 | * @author Administrator 30 | * @Created at 2017/11/5 0005 22:11 31 | * @des 32 | */ 33 | 34 | public class SportHook implements IXposedHookLoadPackage, IXposedHookZygoteInit { 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/bean/AliStepRecord.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.bean; 2 | 3 | /** 4 | * @author Administrator 5 | * @Created at 2017/11/26 0026 21:33 6 | * @des 7 | */ 8 | 9 | public class AliStepRecord { 10 | 11 | /** 12 | * biz : alipay 13 | * steps : 6098 14 | * time : 1511671884723 15 | */ 16 | 17 | private String biz; 18 | private int steps; 19 | private long time; 20 | 21 | public AliStepRecord() { 22 | this.biz = "alipay"; 23 | } 24 | 25 | public String getBiz() { 26 | return biz; 27 | } 28 | 29 | public void setBiz(String biz) { 30 | this.biz = biz; 31 | } 32 | 33 | public int getSteps() { 34 | return steps; 35 | } 36 | 37 | public void setSteps(int steps) { 38 | this.steps = steps; 39 | } 40 | 41 | public long getTime() { 42 | return time; 43 | } 44 | 45 | public void setTime(long time) { 46 | this.time = time; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "AliStepRecord{" + 52 | "biz='" + biz + '\'' + 53 | ", steps=" + steps + 54 | ", time=" + time + 55 | '}'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/fragment/AliFragment.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.fragment; 2 | 3 | import com.qianbajin.sportaccelerator.R; 4 | /** 5 | * @author Administrator 6 | * @Created at 2017/12/4 0004 23:31 7 | * @des 8 | */ 9 | 10 | public class AliFragment extends BasePreferenceFragment { 11 | 12 | @Override 13 | protected int getXmlId() { 14 | return R.xml.config_alipay; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/fragment/AppListFragment.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.fragment; 2 | 3 | import android.app.Activity; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Bundle; 7 | import android.preference.Preference; 8 | import android.preference.PreferenceScreen; 9 | import android.widget.Toast; 10 | 11 | import com.qianbajin.sportaccelerator.ui.ConfigActivity; 12 | import com.qianbajin.sportaccelerator.Constant; 13 | import com.qianbajin.sportaccelerator.R; 14 | import com.qianbajin.sportaccelerator.v4.PreferenceFragment; 15 | /** 16 | * @author Administrator 17 | * @Created at 2017/11/26 0026 17:26 18 | * @des 19 | */ 20 | 21 | public class AppListFragment extends PreferenceFragment { 22 | 23 | private String[] mAppPkList; 24 | 25 | @Override 26 | public void onCreate(Bundle paramBundle) { 27 | super.onCreate(paramBundle); 28 | Activity activity = getActivity(); 29 | addPreferencesFromResource(R.xml.app_list); 30 | PreferenceScreen screen = (PreferenceScreen) findPreference("ps_root"); 31 | PackageManager pm = getActivity().getPackageManager(); 32 | mAppPkList = Constant.SUPPORT_APP_PK_LIST; 33 | try { 34 | for (String pk : mAppPkList) { 35 | ApplicationInfo info = pm.getApplicationInfo(pk, PackageManager.GET_META_DATA); 36 | Preference preference = new Preference(activity); 37 | preference.setTitle(info.loadLabel(pm)); 38 | preference.setIcon(info.loadIcon(pm)); 39 | preference.setKey(pk); 40 | screen.addPreference(preference); 41 | } 42 | } catch (PackageManager.NameNotFoundException e) { 43 | Toast.makeText(activity, "请检查是否安装了支付宝和QQ", Toast.LENGTH_SHORT).show(); 44 | e.printStackTrace(); 45 | } 46 | } 47 | 48 | @Override 49 | public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 50 | ConfigActivity.show(getActivity(), preference.getKey()); 51 | return super.onPreferenceTreeClick(preferenceScreen, preference); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/fragment/BasePreferenceFragment.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.fragment; 2 | 3 | import android.content.SharedPreferences; 4 | import android.os.Bundle; 5 | import android.preference.EditTextPreference; 6 | import android.preference.Preference; 7 | import android.preference.PreferenceManager; 8 | import android.preference.PreferenceScreen; 9 | import android.util.Log; 10 | import android.widget.EditText; 11 | 12 | import com.qianbajin.sportaccelerator.v4.PreferenceFragment; 13 | /** 14 | * @author Administrator 15 | * @Created at 2017/12/7 0007 23:46 16 | * @des 17 | */ 18 | 19 | public abstract class BasePreferenceFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { 20 | 21 | public static final String TAG = "BasePreferenceFragment"; 22 | protected SharedPreferences mSp; 23 | 24 | @Override 25 | public void onCreate(Bundle paramBundle) { 26 | super.onCreate(paramBundle); 27 | addPreferencesFromResource(getXmlId()); 28 | mSp = PreferenceManager.getDefaultSharedPreferences(getActivity()); 29 | PreferenceScreen screen = getPreferenceScreen(); 30 | if (screen != null) { 31 | int preferenceCount = screen.getPreferenceCount(); 32 | for (int i = 0; i < preferenceCount; i++) { 33 | Preference preference = screen.getPreference(i); 34 | if (preference instanceof EditTextPreference) { 35 | onSharedPreferenceChanged(mSp, preference.getKey()); 36 | } 37 | } 38 | } 39 | init(); 40 | } 41 | 42 | protected void init() { 43 | } 44 | 45 | /** 46 | * 获取布局ID 47 | * 48 | * @return 49 | */ 50 | protected abstract int getXmlId(); 51 | 52 | @Override 53 | public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 54 | if (preference instanceof EditTextPreference) { 55 | EditText editText = ((EditTextPreference) preference).getEditText(); 56 | editText.setSelection(editText.getText().length()); 57 | } else if (preference instanceof Preference) { 58 | onPreferenceClick(preference); 59 | } else { 60 | 61 | } 62 | return super.onPreferenceTreeClick(preferenceScreen, preference); 63 | } 64 | 65 | protected void onPreferenceClick(Preference preference) { 66 | } 67 | 68 | protected boolean addPreferenceChangeListener() { 69 | return true; 70 | } 71 | 72 | @Override 73 | public void onResume() { 74 | super.onResume(); 75 | Log.d(TAG, "onResume"); 76 | if (addPreferenceChangeListener()) { 77 | getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 78 | } 79 | } 80 | 81 | @Override 82 | public void onPause() { 83 | super.onPause(); 84 | Log.d(TAG, "onPause"); 85 | if (addPreferenceChangeListener()) { 86 | getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); 87 | } 88 | } 89 | 90 | @Override 91 | public void onSharedPreferenceChanged(SharedPreferences sp, String key) { 92 | Log.d(TAG, "onSharedPreferenceChanged:" + key); 93 | Preference preference = findPreference(key); 94 | if (preference != null && preference instanceof EditTextPreference) { 95 | preference.setSummary(sp.getString(key, "30000")); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/fragment/QQFragment.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.fragment; 2 | 3 | import com.qianbajin.sportaccelerator.R; 4 | /** 5 | * @author Administrator 6 | * @Created at 2017/12/4 0004 23:32 7 | * @des 8 | */ 9 | 10 | public class QQFragment extends BasePreferenceFragment { 11 | 12 | @Override 13 | protected int getXmlId() { 14 | return R.xml.config_qq; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/fragment/SettingFragment.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.fragment; 2 | 3 | import com.qianbajin.sportaccelerator.R; 4 | /** 5 | * @author Administrator 6 | * @Created at 2017/11/26 0026 11:38 7 | * @des 8 | */ 9 | 10 | public class SettingFragment extends BasePreferenceFragment { 11 | 12 | @Override 13 | protected int getXmlId() { 14 | return R.xml.setting_preference; 15 | } 16 | 17 | @Override 18 | protected boolean addPreferenceChangeListener() { 19 | return false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/ui/ConfigActivity.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.ui; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.app.ActionBar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.MenuItem; 10 | 11 | import com.qianbajin.sportaccelerator.Constant; 12 | import com.qianbajin.sportaccelerator.R; 13 | import com.qianbajin.sportaccelerator.fragment.AliFragment; 14 | import com.qianbajin.sportaccelerator.fragment.QQFragment; 15 | 16 | /** 17 | * @author Administrator 18 | * @Created at 2017/12/4 0004 23:37 19 | * @des 20 | */ 21 | public class ConfigActivity extends AppCompatActivity { 22 | 23 | public static final String ARG = "arg"; 24 | 25 | public static void show(Context context, String packageName) { 26 | Intent intent = new Intent(context, ConfigActivity.class); 27 | intent.putExtra(ConfigActivity.ARG, packageName); 28 | context.startActivity(intent); 29 | } 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_config); 35 | ActionBar actionBar = getSupportActionBar(); 36 | if (actionBar != null) { 37 | actionBar.setDisplayHomeAsUpEnabled(true); 38 | } 39 | String pkg = getIntent().getStringExtra(ARG); 40 | Fragment fragment = null; 41 | String title = ""; 42 | if (pkg.equals(Constant.PKG_ALIPAY)) { 43 | fragment = new AliFragment(); 44 | title = getString(R.string.alipay); 45 | } else { 46 | fragment = new QQFragment(); 47 | title = getString(R.string.qq); 48 | } 49 | setTitle(title); 50 | getSupportFragmentManager().beginTransaction().replace(R.id.fl_content, fragment).commit(); 51 | 52 | } 53 | 54 | @Override 55 | public boolean onOptionsItemSelected(MenuItem item) { 56 | switch (item.getItemId()) { 57 | case android.R.id.home: 58 | onBackPressed(); 59 | default: 60 | return super.onOptionsItemSelected(item); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentManager; 7 | import android.support.v4.app.FragmentStatePagerAdapter; 8 | import android.support.v4.view.ViewPager; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.util.Log; 11 | 12 | import com.qianbajin.sportaccelerator.R; 13 | import com.qianbajin.sportaccelerator.fragment.AppListFragment; 14 | import com.qianbajin.sportaccelerator.fragment.SettingFragment; 15 | 16 | import java.io.IOException; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private static final String SP_PATH = "chmod 654 /data/data/com.qianbajin.sportaccelerator/shared_prefs/com.qianbajin.sportaccelerator_preferences.xml"; 21 | private static final String SP_PATH_P = "chmod 755 /data/data/com.qianbajin.sportaccelerator/shared_prefs"; 22 | private static final String SP_PATH_PP = "chmod 755 /data/data/com.qianbajin.sportaccelerator"; 23 | 24 | @Override 25 | protected void onCreate(final Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | 29 | TabLayout tabLayout = (TabLayout) findViewById(R.id.tab); 30 | ViewPager viewPager = (ViewPager) findViewById(R.id.vp_content); 31 | tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.app))); 32 | tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.setting))); 33 | 34 | String[] strings = getResources().getStringArray(R.array.adapt_title); 35 | 36 | viewPager.setAdapter(new PaperAdapter(getSupportFragmentManager(), strings)); 37 | 38 | } 39 | 40 | private boolean chmod() { 41 | try { 42 | Runtime runtime = Runtime.getRuntime(); 43 | runtime.exec(SP_PATH_PP); 44 | runtime.exec(SP_PATH_P); 45 | runtime.exec(SP_PATH); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | return false; 49 | } 50 | return true; 51 | } 52 | 53 | @Override 54 | public void onBackPressed() { 55 | chmod(); 56 | super.onBackPressed(); 57 | } 58 | 59 | static class PaperAdapter extends FragmentStatePagerAdapter { 60 | 61 | private final String[] mTitle; 62 | 63 | public PaperAdapter(FragmentManager fm, String[] strings) { 64 | super(fm); 65 | mTitle = strings; 66 | } 67 | 68 | @Override 69 | public Fragment getItem(int position) { 70 | Log.d("PaperAdapter", "getItem:" + position); 71 | Fragment fragment; 72 | switch (position) { 73 | case 0: 74 | fragment = new AppListFragment(); 75 | break; 76 | case 1: 77 | default: 78 | fragment = new SettingFragment(); 79 | break; 80 | } 81 | return fragment; 82 | } 83 | 84 | @Override 85 | public int getCount() { 86 | return mTitle.length; 87 | } 88 | 89 | @Override 90 | public CharSequence getPageTitle(int position) { 91 | return mTitle[position]; 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/v4/PreferenceFragment.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.v4; 2 | 3 | import android.content.Intent; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.preference.Preference; 9 | import android.preference.PreferenceManager; 10 | import android.preference.PreferenceScreen; 11 | import android.support.v4.app.Fragment; 12 | import android.view.KeyEvent; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.AdapterView; 17 | import android.widget.ListView; 18 | 19 | import com.qianbajin.sportaccelerator.R; 20 | 21 | import java.lang.reflect.InvocationTargetException; 22 | import java.lang.reflect.Method; 23 | /** 24 | * @author Administrator 25 | * @Created at 2017/12/1 0001 23:44 26 | * @des 27 | */ 28 | 29 | public class PreferenceFragment extends Fragment implements 30 | PreferenceManagerCompat.OnPreferenceTreeClickListener{ 31 | 32 | private static final String PREFERENCES_TAG = "android:preferences"; 33 | 34 | private PreferenceManager mPreferenceManager; 35 | private ListView mList; 36 | private boolean mHavePrefs; 37 | private boolean mInitDone; 38 | 39 | /** 40 | * The starting request code given out to preference framework. 41 | */ 42 | private static final int FIRST_REQUEST_CODE = 100; 43 | 44 | private static final int MSG_BIND_PREFERENCES = 1; 45 | private Handler mHandler = new Handler() { 46 | @Override 47 | public void handleMessage(Message msg) { 48 | switch (msg.what) { 49 | 50 | case MSG_BIND_PREFERENCES: 51 | bindPreferences(); 52 | break; 53 | default: 54 | break; 55 | } 56 | } 57 | }; 58 | 59 | final private Runnable mRequestFocus = new Runnable() { 60 | @Override 61 | public void run() { 62 | mList.focusableViewAvailable(mList); 63 | } 64 | }; 65 | 66 | /** 67 | * Interface that PreferenceFragment's containing activity should 68 | * implement to be able to process preference items that wish to 69 | * switch to a new fragment. 70 | */ 71 | public interface OnPreferenceStartFragmentCallback { 72 | /** 73 | * Called when the user has clicked on a Preference that has 74 | * a fragment class name associated with it. The implementation 75 | * to should instantiate and switch to an instance of the given 76 | * fragment. 77 | */ 78 | boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref); 79 | } 80 | 81 | @Override 82 | public void onCreate(Bundle paramBundle) { 83 | super.onCreate(paramBundle); 84 | mPreferenceManager = PreferenceManagerCompat.newInstance(getActivity(), FIRST_REQUEST_CODE); 85 | PreferenceManagerCompat.setFragment(mPreferenceManager, this); 86 | } 87 | 88 | @Override 89 | public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, 90 | Bundle paramBundle) { 91 | return paramLayoutInflater.inflate(R.layout.preference_list_fragment, paramViewGroup, 92 | false); 93 | } 94 | 95 | @Override 96 | public void onActivityCreated(Bundle savedInstanceState) { 97 | super.onActivityCreated(savedInstanceState); 98 | 99 | if (mHavePrefs) { 100 | bindPreferences(); 101 | } 102 | 103 | mInitDone = true; 104 | 105 | if (savedInstanceState != null) { 106 | Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); 107 | if (container != null) { 108 | final PreferenceScreen preferenceScreen = getPreferenceScreen(); 109 | if (preferenceScreen != null) { 110 | preferenceScreen.restoreHierarchyState(container); 111 | } 112 | } 113 | } 114 | } 115 | 116 | @Override 117 | public void onStart() { 118 | super.onStart(); 119 | PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, this); 120 | } 121 | 122 | @Override 123 | public void onStop() { 124 | super.onStop(); 125 | PreferenceManagerCompat.dispatchActivityStop(mPreferenceManager); 126 | PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, null); 127 | } 128 | 129 | @Override 130 | public void onDestroyView() { 131 | mList = null; 132 | mHandler.removeCallbacks(mRequestFocus); 133 | mHandler.removeMessages(MSG_BIND_PREFERENCES); 134 | super.onDestroyView(); 135 | } 136 | 137 | @Override 138 | public void onDestroy() { 139 | super.onDestroy(); 140 | PreferenceManagerCompat.dispatchActivityDestroy(mPreferenceManager); 141 | } 142 | 143 | @Override 144 | public void onSaveInstanceState(Bundle outState) { 145 | super.onSaveInstanceState(outState); 146 | 147 | final PreferenceScreen preferenceScreen = getPreferenceScreen(); 148 | if (preferenceScreen != null) { 149 | Bundle container = new Bundle(); 150 | preferenceScreen.saveHierarchyState(container); 151 | outState.putBundle(PREFERENCES_TAG, container); 152 | } 153 | } 154 | 155 | @Override 156 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 157 | super.onActivityResult(requestCode, resultCode, data); 158 | 159 | PreferenceManagerCompat.dispatchActivityResult(mPreferenceManager, requestCode, resultCode, data); 160 | } 161 | 162 | /** 163 | * Returns the {@link PreferenceManager} used by this fragment. 164 | * @return The {@link PreferenceManager}. 165 | */ 166 | public PreferenceManager getPreferenceManager() { 167 | return mPreferenceManager; 168 | } 169 | 170 | /** 171 | * Sets the root of the preference hierarchy that this fragment is showing. 172 | * 173 | * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. 174 | */ 175 | public void setPreferenceScreen(PreferenceScreen preferenceScreen) { 176 | if (PreferenceManagerCompat.setPreferences(mPreferenceManager, preferenceScreen) && preferenceScreen != null) { 177 | mHavePrefs = true; 178 | if (mInitDone) { 179 | postBindPreferences(); 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * Gets the root of the preference hierarchy that this fragment is showing. 186 | * 187 | * @return The {@link PreferenceScreen} that is the root of the preference 188 | * hierarchy. 189 | */ 190 | public PreferenceScreen getPreferenceScreen() { 191 | return PreferenceManagerCompat.getPreferenceScreen(mPreferenceManager); 192 | } 193 | 194 | /** 195 | * Adds preferences from activities that match the given {@link Intent}. 196 | * 197 | * @param intent The {@link Intent} to query activities. 198 | */ 199 | public void addPreferencesFromIntent(Intent intent) { 200 | requirePreferenceManager(); 201 | 202 | setPreferenceScreen(PreferenceManagerCompat.inflateFromIntent(mPreferenceManager, intent, getPreferenceScreen())); 203 | } 204 | 205 | /** 206 | * Inflates the given XML resource and adds the preference hierarchy to the current 207 | * preference hierarchy. 208 | * 209 | * @param preferencesResId The XML resource ID to inflate. 210 | */ 211 | public void addPreferencesFromResource(int preferencesResId) { 212 | requirePreferenceManager(); 213 | 214 | setPreferenceScreen(PreferenceManagerCompat.inflateFromResource(mPreferenceManager, getActivity(), 215 | preferencesResId, getPreferenceScreen())); 216 | } 217 | 218 | /** 219 | * {@inheritDoc} 220 | */ 221 | @Override 222 | public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, 223 | Preference preference) { 224 | //if (preference.getFragment() != null && 225 | if ( 226 | getActivity() instanceof OnPreferenceStartFragmentCallback) { 227 | return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment( 228 | this, preference); 229 | } 230 | return false; 231 | } 232 | 233 | /** 234 | * Finds a {@link Preference} based on its key. 235 | * 236 | * @param key The key of the preference to retrieve. 237 | * @return The {@link Preference} with the key, or null. 238 | * @see PreferenceGroup#findPreference(CharSequence) 239 | */ 240 | public Preference findPreference(CharSequence key) { 241 | if (mPreferenceManager == null) { 242 | return null; 243 | } 244 | return mPreferenceManager.findPreference(key); 245 | } 246 | 247 | private void requirePreferenceManager() { 248 | if (mPreferenceManager == null) { 249 | throw new RuntimeException("This should be called after super.onCreate."); 250 | } 251 | } 252 | 253 | private void postBindPreferences() { 254 | if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) { 255 | return; 256 | } 257 | mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); 258 | } 259 | 260 | private void bindPreferences() { 261 | final PreferenceScreen preferenceScreen = getPreferenceScreen(); 262 | if (preferenceScreen != null) { 263 | preferenceScreen.bind(getListView()); 264 | } 265 | 266 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { 267 | // Workaround android bug for SDK 10 and below - see 268 | // https://github.com/android/platform_frameworks_base/commit/2d43d283fc0f22b08f43c6db4da71031168e7f59 269 | getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { 270 | @Override 271 | public void onItemClick(AdapterView parent, View view, int position, long id) { 272 | // If the list has headers, subtract them from the index. 273 | if (parent instanceof ListView) { 274 | position -= ((ListView)parent).getHeaderViewsCount(); 275 | } 276 | 277 | Object item = preferenceScreen.getRootAdapter().getItem(position); 278 | if (!(item instanceof Preference)) { 279 | return; 280 | } 281 | 282 | final Preference preference = (Preference)item; 283 | try { 284 | Method performClick = Preference.class.getDeclaredMethod( 285 | "performClick", PreferenceScreen.class); 286 | performClick.setAccessible(true); 287 | performClick.invoke(preference, preferenceScreen); 288 | } catch (InvocationTargetException e) { 289 | } catch (IllegalAccessException e) { 290 | } catch (NoSuchMethodException e) { 291 | } 292 | } 293 | }); 294 | } 295 | } 296 | 297 | public ListView getListView() { 298 | ensureList(); 299 | return mList; 300 | } 301 | 302 | private void ensureList() { 303 | if (mList != null) { 304 | return; 305 | } 306 | View root = getView(); 307 | if (root == null) { 308 | throw new IllegalStateException("Content view not yet created"); 309 | } 310 | View rawListView = root.findViewById(android.R.id.list); 311 | if (!(rawListView instanceof ListView)) { 312 | throw new RuntimeException( 313 | "Content has view with id attribute 'android.R.id.list' " 314 | + "that is not a ListView class"); 315 | } 316 | mList = (ListView)rawListView; 317 | if (mList == null) { 318 | throw new RuntimeException( 319 | "Your content must have a ListView whose id attribute is " + 320 | "'android.R.id.list'"); 321 | } 322 | mList.setOnKeyListener(mListOnKeyListener); 323 | mHandler.post(mRequestFocus); 324 | } 325 | 326 | private View.OnKeyListener mListOnKeyListener = new View.OnKeyListener() { 327 | 328 | @Override 329 | public boolean onKey(View v, int keyCode, KeyEvent event) { 330 | Object selectedItem = mList.getSelectedItem(); 331 | if (selectedItem instanceof Preference) { 332 | @SuppressWarnings("unused") 333 | View selectedView = mList.getSelectedView(); 334 | //return ((Preference)selectedItem).onKey( 335 | // selectedView, keyCode, event); 336 | return false; 337 | } 338 | return false; 339 | } 340 | 341 | }; 342 | } 343 | -------------------------------------------------------------------------------- /app/src/main/java/com/qianbajin/sportaccelerator/v4/PreferenceManagerCompat.java: -------------------------------------------------------------------------------- 1 | package com.qianbajin.sportaccelerator.v4; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.preference.Preference; 7 | import android.preference.PreferenceManager; 8 | import android.preference.PreferenceScreen; 9 | import android.util.Log; 10 | 11 | import java.lang.reflect.Constructor; 12 | import java.lang.reflect.Field; 13 | import java.lang.reflect.InvocationHandler; 14 | import java.lang.reflect.Method; 15 | import java.lang.reflect.Proxy; 16 | /** 17 | * @author Administrator 18 | * @Created at 2017/12/1 0001 23:45 19 | * @des 20 | */ 21 | 22 | public class PreferenceManagerCompat { 23 | private static final String TAG = PreferenceManagerCompat.class.getSimpleName(); 24 | 25 | /** 26 | * Interface definition for a callback to be invoked when a 27 | * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is 28 | * clicked. 29 | */ 30 | interface OnPreferenceTreeClickListener { 31 | /** 32 | * Called when a preference in the tree rooted at this 33 | * {@link PreferenceScreen} has been clicked. 34 | * 35 | * @param preferenceScreen The {@link PreferenceScreen} that the 36 | * preference is located in. 37 | * @param preference The preference that was clicked. 38 | * @return Whether the click was handled. 39 | */ 40 | boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference); 41 | } 42 | 43 | static PreferenceManager newInstance(Activity activity, int firstRequestCode) { 44 | try { 45 | Constructor c = PreferenceManager.class.getDeclaredConstructor(Activity.class, int.class); 46 | c.setAccessible(true); 47 | return c.newInstance(activity, firstRequestCode); 48 | } catch (Exception e) { 49 | Log.w(TAG, "Couldn't call constructor PreferenceManager by reflection", e); 50 | } 51 | return null; 52 | } 53 | 54 | /** 55 | * Sets the owning preference fragment 56 | */ 57 | static void setFragment(PreferenceManager manager, PreferenceFragment fragment) { 58 | // stub 59 | } 60 | 61 | /** 62 | * Sets the callback to be invoked when a {@link Preference} in the 63 | * hierarchy rooted at this {@link PreferenceManager} is clicked. 64 | * 65 | * @param listener The callback to be invoked. 66 | */ 67 | static void setOnPreferenceTreeClickListener(PreferenceManager manager, final OnPreferenceTreeClickListener listener) { 68 | try { 69 | Field onPreferenceTreeClickListener = PreferenceManager.class.getDeclaredField("mOnPreferenceTreeClickListener"); 70 | onPreferenceTreeClickListener.setAccessible(true); 71 | if (listener != null) { 72 | Object proxy = Proxy.newProxyInstance( 73 | onPreferenceTreeClickListener.getType().getClassLoader(), 74 | new Class[] { onPreferenceTreeClickListener.getType() }, 75 | new InvocationHandler() { 76 | @Override 77 | public Object invoke(Object proxy, Method method, Object[] args) { 78 | if (method.getName().equals("onPreferenceTreeClick")) { 79 | return Boolean.valueOf(listener.onPreferenceTreeClick((PreferenceScreen) args[0], (Preference) args[1])); 80 | } else { 81 | return null; 82 | } 83 | } 84 | }); 85 | onPreferenceTreeClickListener.set(manager, proxy); 86 | } else { 87 | onPreferenceTreeClickListener.set(manager, null); 88 | } 89 | } catch (Exception e) { 90 | Log.w(TAG, "Couldn't set PreferenceManager.mOnPreferenceTreeClickListener by reflection", e); 91 | } 92 | } 93 | 94 | /** 95 | * Inflates a preference hierarchy from the preference hierarchies of 96 | * {@link Activity Activities} that match the given {@link Intent}. An 97 | * {@link Activity} defines its preference hierarchy with meta-data using 98 | * the {@link #//METADATA_KEY_PREFERENCES} key. 99 | *

100 | * If a preference hierarchy is given, the new preference hierarchies will 101 | * be merged in. 102 | * 103 | * @param queryIntent The intent to match activities. 104 | * @param rootPreferences Optional existing hierarchy to merge the new 105 | * hierarchies into. 106 | * @return The root hierarchy (if one was not provided, the new hierarchy's 107 | * root). 108 | */ 109 | static PreferenceScreen inflateFromIntent(PreferenceManager manager, Intent queryIntent, PreferenceScreen rootPreferences) { 110 | try { 111 | Method m = PreferenceManager.class.getDeclaredMethod("inflateFromIntent", Intent.class, PreferenceScreen.class); 112 | m.setAccessible(true); 113 | PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(manager, queryIntent, rootPreferences); 114 | return prefScreen; 115 | } catch (Exception e) { 116 | Log.w(TAG, "Couldn't call PreferenceManager.inflateFromIntent by reflection", e); 117 | } 118 | return null; 119 | } 120 | 121 | /** 122 | * Inflates a preference hierarchy from XML. If a preference hierarchy is 123 | * given, the new preference hierarchies will be merged in. 124 | * 125 | * @param context The context of the resource. 126 | * @param resId The resource ID of the XML to inflate. 127 | * @param rootPreferences Optional existing hierarchy to merge the new 128 | * hierarchies into. 129 | * @return The root hierarchy (if one was not provided, the new hierarchy's 130 | * root). 131 | * @hide 132 | */ 133 | static PreferenceScreen inflateFromResource(PreferenceManager manager, Activity context, int resId, PreferenceScreen rootPreferences) { 134 | try { 135 | Method m = PreferenceManager.class.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class); 136 | m.setAccessible(true); 137 | PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(manager, context, resId, rootPreferences); 138 | return prefScreen; 139 | } catch (Exception e) { 140 | Log.w(TAG, "Couldn't call PreferenceManager.inflateFromResource by reflection", e); 141 | } 142 | return null; 143 | } 144 | 145 | /** 146 | * Returns the root of the preference hierarchy managed by this class. 147 | * 148 | * @return The {@link PreferenceScreen} object that is at the root of the hierarchy. 149 | */ 150 | static PreferenceScreen getPreferenceScreen(PreferenceManager manager) { 151 | try { 152 | Method m = PreferenceManager.class.getDeclaredMethod("getPreferenceScreen"); 153 | m.setAccessible(true); 154 | return (PreferenceScreen) m.invoke(manager); 155 | } catch (Exception e) { 156 | Log.w(TAG, "Couldn't call PreferenceManager.getPreferenceScreen by reflection", e); 157 | } 158 | return null; 159 | } 160 | 161 | /** 162 | * Called by the {@link PreferenceManager} to dispatch a subactivity result. 163 | */ 164 | static void dispatchActivityResult(PreferenceManager manager, int requestCode, int resultCode, Intent data) { 165 | try { 166 | Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityResult", int.class, int.class, Intent.class); 167 | m.setAccessible(true); 168 | m.invoke(manager, requestCode, resultCode, data); 169 | } catch (Exception e) { 170 | Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityResult by reflection", e); 171 | } 172 | } 173 | 174 | /** 175 | * Called by the {@link PreferenceManager} to dispatch the activity stop 176 | * event. 177 | */ 178 | static void dispatchActivityStop(PreferenceManager manager) { 179 | try { 180 | Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityStop"); 181 | m.setAccessible(true); 182 | m.invoke(manager); 183 | } catch (Exception e) { 184 | Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityStop by reflection", e); 185 | } 186 | } 187 | 188 | /** 189 | * Called by the {@link PreferenceManager} to dispatch the activity destroy 190 | * event. 191 | */ 192 | static void dispatchActivityDestroy(PreferenceManager manager) { 193 | try { 194 | Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityDestroy"); 195 | m.setAccessible(true); 196 | m.invoke(manager); 197 | } catch (Exception e) { 198 | Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityDestroy by reflection", e); 199 | } 200 | } 201 | 202 | /** 203 | * Sets the root of the preference hierarchy. 204 | * 205 | * @param screen The root {@link PreferenceScreen} of the preference hierarchy. 206 | * @return Whether the {@link PreferenceScreen} given is different than the previous. 207 | */ 208 | static boolean setPreferences(PreferenceManager manager, PreferenceScreen screen) { 209 | try { 210 | Method m = PreferenceManager.class.getDeclaredMethod("setPreferences", PreferenceScreen.class); 211 | m.setAccessible(true); 212 | return ((Boolean) m.invoke(manager, screen)); 213 | } catch (Exception e) { 214 | Log.w(TAG, "Couldn't call PreferenceManager.setPreferences by reflection", e); 215 | } 216 | return false; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | 28 | 29 | 30 | 31 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/preference_list_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 25 | 26 | 39 | 40 | 46 | 47 | 52 | 53 |