package com.timecat.xposed; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; import de.robv.android.xposed.XSharedPreferences; import de.robv.android.xposed.callbacks.XC_LoadPackage; import static com.timecat.xposed.TouchEventHandler.BIG_BANG_RESPONSE_TIME; import static com.timecat.xposed.XposedConstant.PACKAGE_NAME; import static com.timecat.xposed.XposedConstant.SP_DOBLUE_CLICK; import static com.timecat.xposed.XposedConstant.SP_NAME; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; /** * Created by dim on 16/10/23. */ public class XposedBigBang implements IXposedHookLoadPackage { private static final String TAG = "XposedBigBang"; public static final int NON_SELECTION=3; private final TouchEventHandler mTouchHandler = new TouchEventHandler(); private final TouchEventHandler mTouchTextViewHandler = new TouchEventHandler(); private final XposedUniversalCopyHandler mUniversalCopyHandler = new XposedUniversalCopyHandler(); private final List<Filter> mFilters = new ArrayList<>(); private XSharedPreferences appXSP; @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { Log.e("timecat", "xposed-packageName:" + loadPackageParam.packageName); setXpoedEnable(loadPackageParam); // if (!new File("/data/data/"+PACKAGE_NAME).exists()) // return; // wakeup(loadPackageParam); // Logger.d(TAG, loadPackageParam.packageName); appXSP = new XSharedPreferences(PACKAGE_NAME, SP_NAME); appXSP.makeWorldReadable(); int type =appXSP.getInt(loadPackageParam.packageName,NON_SELECTION); // if (type==NON_SELECTION){ // return; // } // XSharedPreferences appSp = new XSharedPreferences(PACKAGE_NAME, MAINSPNAME); // appSp.makeWorldReadable(); //只用点击 mFilters.add(new Filter.TextViewValidFilter()); //优化微信 下的体验。 if ("com.tencent.mm".equals(loadPackageParam.packageName)) { //朋友圈内容拦截。 mFilters.add(new Filter.WeChatValidFilter(loadPackageParam.classLoader)); //聊天详情中的文字点击事件优化 try { findAndHookMethod(loadPackageParam.classLoader.loadClass("com.tencent.mm.ui.base.MMTextView"), "onTouchEvent", MotionEvent.class, new MMTextViewTouchEvent()); } catch (ClassNotFoundException e) { e.printStackTrace(); } try { findAndHookMethod(loadPackageParam.classLoader.loadClass("com.tencent.mm.ui.widget.MMTextView"), "onTouchEvent", MotionEvent.class, new MMTextViewTouchEvent()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } mUniversalCopyHandler.setFilters(mFilters); // installer 不注入。 防止代码出错。进不去installer 中。 if (!"de.robv.android.xposed.installer".equals(loadPackageParam.packageName) && !"com.android.systemui".equals(loadPackageParam.packageName)) { if (type!=NON_SELECTION){ findAndHookMethod(Activity.class, "onTouchEvent", MotionEvent.class, new ActivityTouchEvent()); findAndHookMethod(View.class, "dispatchTouchEvent", MotionEvent.class, new ViewTouchEvent(loadPackageParam.packageName,type)); findAndHookMethod(View.class, "setOnClickListener", View.OnClickListener.class, new ViewOnClickListenerHooker(loadPackageParam.packageName,type)); findAndHookMethod(View.class, "setOnLongClickListener", View.OnLongClickListener.class, new ViewOnLongClickListenerHooker(loadPackageParam.packageName,type)); } findAndHookMethod(Activity.class, "onStart", new UniversalCopyOnStartHook()); findAndHookMethod(Activity.class, "onStop", new UniversalCopyOnStopHook()); } } // } else { // //使用force touch // mFilters.add(new Filter.TextViewValidFilter()); // //优化微信 下的体验。 // mFilters.add(new Filter.WeChatValidFilter(loadPackageParam.classLoader)); // // // installer 不注入。 防止代码出错。进不去installer 中。 // if (!"de.robv.android.xposed.installer".equals(loadPackageParam.packageName)) { // // findAndHookMethod(Activity.class, "onTouchEvent", MotionEvent.class, new ActivityTouchEvent()); // findAndHookMethod(TextView.class, "onTouchEvent", MotionEvent.class, new ForceTouchTextViewTouchEvent(loadPackageParam.packageName)); // } // } // private void wakeup(XC_LoadPackage.LoadPackageParam loadPackageParam) { // findAndHookMethod("android.app.Application", loadPackageParam.classLoader, "onCreate", new XC_MethodHook() { // @Override // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // Context context = (Context) param.thisObject; // // Intent intent = new Intent(); // intent.setAction("com.timecat.bigbang.wake"); // intent.addCategory(Intent.CATEGORY_DEFAULT); // context.sendBroadcast(intent); // } // // @Override // protected void afterHookedMethod(MethodHookParam param) throws Throwable { // super.afterHookedMethod(param); // } // }); // } private void setXpoedEnable(XC_LoadPackage.LoadPackageParam loadPackageParam) throws ClassNotFoundException { if (loadPackageParam.packageName.equals(PACKAGE_NAME)) { findAndHookMethod(loadPackageParam.classLoader.loadClass("com.timecat.xposed.XposedEnable"), "isEnable", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable { return true; } }); } } private Set<String> getLauncherAsWhiteList(Context c) { HashSet<String> packages = new HashSet<>(); PackageManager packageManager = c.getPackageManager(); final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); // final ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0); List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo ri : resolveInfo) { packages.add(ri.activityInfo.packageName); } return packages; } private Set<String> getInputMethodAsWhiteList(Context context) { HashSet<String> packages = new HashSet<>(); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); List<InputMethodInfo> methodList = imm.getInputMethodList(); for (InputMethodInfo info : methodList) { packages.add(info.getPackageName()); } return packages; } private class MMTextViewTouchEvent extends XC_MethodHook { private boolean intercept = false; @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); //拦截聊天界面快速点击进入信息详情 View view = (View) param.thisObject; MotionEvent event = (MotionEvent) param.args[0]; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: long preClickTimeMillis = mTouchHandler.getClickTimeMillis(view); long currentTimeMillis = System.currentTimeMillis(); if (preClickTimeMillis != 0) { long interval = currentTimeMillis - preClickTimeMillis; if (interval < BIG_BANG_RESPONSE_TIME) { intercept = true; } else { intercept = false; } } else { intercept = false; } break; } mTouchHandler.hookTouchEvent(view, event, mFilters, true, appXSP.getInt(SP_DOBLUE_CLICK,1000)); if (intercept) { param.setResult(true); } } } private class ForceTouchTextViewTouchEvent extends XC_MethodHook { private final String packageName; public ForceTouchTextViewTouchEvent(String packageName) { this.packageName = packageName; } @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); //拦截聊天界面快速点击进入信息详情 View view = (View) param.thisObject; MotionEvent event = (MotionEvent) param.args[0]; if (isKeyBoardOrLauncher(view.getContext(), packageName)) return; // boolean intercept = mTouchHandler.hookForceTouchEvent(view, event, mFilters, true); // if (intercept) { // param.setResult(true); // } } } private class TextViewTouchEvent extends XC_MethodHook { private final String packageName; private boolean intercept; public TextViewTouchEvent(String packageName) { this.packageName = packageName; } @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); //拦截聊天界面快速点击进入信息详情 View view = (View) param.thisObject; MotionEvent event = (MotionEvent) param.args[0]; Log.e("timecat", "xposed-packageName:" + event); if (isKeyBoardOrLauncher(view.getContext(), packageName)) return; // switch (event.getAction()) { // case MotionEvent.ACTION_DOWN: // long preClickTimeMillis = mTouchTextViewHandler.getViewClickTimeMillis(view); // long currentTimeMillis = System.currentTimeMillis(); // long interval = currentTimeMillis - preClickTimeMillis; // if (interval < TouchEventHandler.BIG_BANG_RESPONSE_TIME) { // intercept = true; // } else { // intercept = false; // } // mTouchTextViewHandler.setViewClickTimeMillis(view,System.currentTimeMillis()); //// } else { //// intercept = false; //// mTouchTextViewHandler.setViewClickTimeMillis(view,System.currentTimeMillis()); //// } // break; // } mTouchTextViewHandler.hookAllTouchEvent(view, event, mFilters, true,appXSP.getInt(SP_DOBLUE_CLICK,1000)); if (intercept) { param.setResult(true); } } } private boolean isKeyBoardOrLauncher=false; private boolean isKeyBoardOrLauncherChecked=false; private boolean isKeyBoardOrLauncher(Context context, String packageName) { if (isKeyBoardOrLauncherChecked){ return isKeyBoardOrLauncher; } if (context == null) { isKeyBoardOrLauncher=true; isKeyBoardOrLauncherChecked=true; return true; } for (String package_process : getInputMethodAsWhiteList(context)) { if (package_process.equals(packageName)) { isKeyBoardOrLauncher=true; isKeyBoardOrLauncherChecked=true; return true; } } for (String package_process : getLauncherAsWhiteList(context)) { if (package_process.equals(packageName)) { isKeyBoardOrLauncher=true; isKeyBoardOrLauncherChecked=true; return true; } } isKeyBoardOrLauncher=false; isKeyBoardOrLauncherChecked=true; return false; } private void setClickTypeToTouchHandler(int type){ if (type == 0) { mTouchHandler.setUseClick(true); }else if (type == 1){ mTouchHandler.setUseLongClick(true); }else if (type == 2){ mTouchHandler.setUseDoubleClick(true); }else if (type == 3){ } } private class ActivityTouchEvent extends XC_MethodHook { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); Activity activity = (Activity) param.thisObject; View view = activity.findViewById(android.R.id.content); MotionEvent event = (MotionEvent) param.args[0]; // Log.e("timecat", "activityTouchEvent: " + event); //mTouchHandler.hookTouchEvent(view, event, mFilters, false); } } //1. 为什么不hook住onTouch方法呢,而且非要dispatchTouchEvent返回true的时候才进行操作呢? private class ViewTouchEvent extends XC_MethodHook { private final String packageName; Class viewRootImplClass; public ViewTouchEvent(String packageName,int type) { this.packageName = packageName; try { viewRootImplClass = this.getClass().getClassLoader().loadClass("android.view.ViewRootImpl"); } catch (ClassNotFoundException e) { e.printStackTrace(); } setClickTypeToTouchHandler(type); } // @Override // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // super.beforeHookedMethod(param); // View view = (View) param.thisObject; // if (isKeyBoardOrLauncher(view.getContext(), packageName)) // return; // MotionEvent event = (MotionEvent) param.args[0]; // Log.e(TAG,"before->View:"+ view.getClass().getSimpleName()+ " viewTouchEvent: " + event); // } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); View view = (View) param.thisObject; if (isKeyBoardOrLauncher(view.getContext(), packageName)) return; MotionEvent event = (MotionEvent) param.args[0]; // Log.e(TAG,"after->View:"+ view.getClass().getSimpleName()+ " viewTouchEvent: " + event); if ((Boolean) param.getResult() || view.getParent()==null || (viewRootImplClass.isInstance(view.getParent()) )) { mTouchHandler.hookTouchEvent(view, event, mFilters, true, appXSP.getInt(SP_DOBLUE_CLICK, 1000)); } } } private class ViewOnClickListenerHooker extends XC_MethodHook { private final String packageName; public ViewOnClickListenerHooker(String packageName,int type) { this.packageName = packageName; setClickTypeToTouchHandler(type); } @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); View view = (View) param.thisObject; final View.OnClickListener listener = (View.OnClickListener) param.args[0]; if (isKeyBoardOrLauncher(view.getContext(), packageName)) return; // if (TextView.class.isInstance(view)){ // view.setTag(R.id.bigBang_$_click,true); View.OnClickListener newListener=new View.OnClickListener() { @Override public void onClick(View v) { mTouchHandler.hookOnClickListener(v,mFilters); if (listener==null){ return ; }else { listener.onClick(v); } } }; param.args[0]=newListener; // } } // @Override // protected void afterHookedMethod(MethodHookParam param) throws Throwable { // super.afterHookedMethod(param); // View view = (View) param.thisObject; // final View.OnClickListener listener = (View.OnClickListener) param.args[0]; // if (isKeyBoardOrLauncher(view.getContext(), packageName)) // return; // if (TextView.class.isInstance(view)){ // View.OnClickListener newListener=new View.OnClickListener() { // @Override // public void onClick(View v) { // listener.onClick(v); // mTouchHandler.hookOnClickListener(v); // } // }; // view.setOnClickListener(newListener); // } // } } private class ViewOnLongClickListenerHooker extends XC_MethodHook { private final String packageName; public ViewOnLongClickListenerHooker(String packageName,int type) { this.packageName = packageName; setClickTypeToTouchHandler(type); } @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); View view = (View) param.thisObject; final View.OnLongClickListener listener = (View.OnLongClickListener) param.args[0]; if (isKeyBoardOrLauncher(view.getContext(), packageName)) return; // if (TextView.class.isInstance(view)){ View.OnLongClickListener newListener=new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { // Log.e("ViewOnLongClickListener","onLongClick"); mTouchHandler.hookOnLongClickListener(v,mFilters); if (listener==null){ return false; }else { return listener.onLongClick(v); } } }; param.args[0]=newListener; // } } } private class UniversalCopyOnStartHook extends XC_MethodHook { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); Activity activity = (Activity) param.thisObject; mUniversalCopyHandler.onStart(activity); } } private class UniversalCopyOnStopHook extends XC_MethodHook { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); Activity activity = (Activity) param.thisObject; mUniversalCopyHandler.onStop(activity); } } }