All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.imqa.asm.FragmentMakerFinder Maven / Gradle / Ivy

There is a newer version: 2.25.11
Show newest version
package io.imqa.asm;

import org.objectweb.asm.*;

import io.imqa.injector.MappingDecoder;
import io.imqa.injector.util.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

public class FragmentMakerFinder implements Finder {

	HashMap lifeCycle = new HashMap<>(); // onCreateView, onStart, onResume

	public void init() {
		lifeCycle.clear();
		lifeCycle.put("onCreate", false);
		lifeCycle.put("onCreateView", false);
		lifeCycle.put("onStart", false);
		lifeCycle.put("onResume", false);
	}

	public boolean find(String name, String superName, String[] interfaces) {

		if (checkFragmentClass(superName)) {
			if (!checkAndroidClass(name)) {
				Logger.d("SuperClass name", superName);
				return true;
			}
		}

		return false;
	}

	private boolean checkFragmentClass(String name) {
		if (name.equals(MappingDecoder.getInstance().findClass("android/support/v4/app/Fragment"))
				|| name.equals(MappingDecoder.getInstance().findClass("android/support/v4/app/DialogFragment"))
				|| name.equals(MappingDecoder.getInstance().findClass("android/support/v4/app/ListFragment"))
				|| name.equals(MappingDecoder.getInstance().findClass("android/support/v4/app/PreferenceFragment"))
				|| name.equals(MappingDecoder.getInstance().findClass("androidx/fragment/app/Fragment"))
				|| name.equals(MappingDecoder.getInstance().findClass("android/app/Fragment"))) {
			Logger.d("Fragment class", name);
			return true;
		}
		return false;
	}

	private boolean checkAndroidClass(String name) {
		String[] steps = name.split("/");

		if (steps[0].equals("android")) return true;

		String packageName = name.substring(0, name.lastIndexOf("/"));
		if (packageName.equals(MappingDecoder.getInstance().findClass("android/support/v7/app"))
				|| packageName.equals(MappingDecoder.getInstance().findClass("android/support/v4/app"))
                || packageName.equals(MappingDecoder.getInstance().findClass("androidx/fragment/app"))) {
			return true;
		}

		return false;
	}

	private boolean checkMethod(String name, String[] allowedNameList) {
		for(String allowedName : allowedNameList) {
			if (name.equals(allowedName))
				return true;
		}

		return false;
	}

	private String[] getFragmentMethod() {
		List methodList = new ArrayList();

		//methodList.add("onAttach");
		methodList.add("onCreate");
		methodList.add("onCreateView");
		methodList.add("onActivityCreated");
		methodList.add("onViewStateRestored");
		methodList.add("onStart");
		methodList.add("onResume");

		/*
		methodList.add("onPause");
		methodList.add("onStop");
		methodList.add("onDestroyView");
		methodList.add("onDestroy");
		methodList.add("onDetach");
		*/

		String[] methodArray = new String[methodList.size()];
		methodList.toArray(methodArray);
		return methodArray;
	}

	public MethodVisitor getVisitor(String[] interfaceList, String className, String superName, 
		int access, String name, String desc, MethodVisitor mv) {

		if (find(className, superName, interfaceList)
			&& checkMethod(name, getFragmentMethod())) {
			Logger.d("Fragment Inject", className + ", Method : "+name);

			setVisited(name);
			return new FragmentVisitor(className, access, name, desc, mv); 
		}

		for (String interfaceName : interfaceList) {
			if (find(className, interfaceName, interfaceList)
				&& checkMethod(name, getFragmentMethod())) {
				Logger.d("Fragment Inject", className + ", Method : "+name);
				setVisited(name);
				return new FragmentVisitor(className, access, name, desc, mv); 
			}
		}

		return mv;
	}

	@Override
	public void visitEnd(ClassVisitor cv, String className, String superClass, String[] interfaceList) {
		boolean visited = false;
		if (find(className, superClass, interfaceList)) {
			makeVisit(cv, className, superClass);
			visited = true;
		}

		for (String interfaceName : interfaceList) {
			if (find(className, interfaceName, interfaceList) && !visited) {
				makeVisit(cv, className, superClass);
				visited = true;
			}
		}

		cv.visitEnd();
	}

	protected void setVisited(String name) {
		switch (name) {
			case "onCreate":
			case "onCreateView":
			case "onStart":
			case "onResume":
				lifeCycle.replace(name, true);
				break;
		}
	}

	protected void makeVisit(ClassVisitor cv, String className, String superClass) {
		// 프래그먼트 클래스에 Life cycle 구현 안된 곳이 있다면 새로 작성
		Set cycleNames = lifeCycle.keySet();
		for(String cycleName : cycleNames) {
			if (!lifeCycle.get(cycleName)) {
				String desc = "()V";
				switch (cycleName) {
					case "onCreate": 
						desc = MappingDecoder.getInstance().decodeDesc("(Landroid/os/Bundle;)V");
						break;
					case "onCreateView":
						desc = MappingDecoder.getInstance().decodeDesc("(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View;");
						break;
				}

				FragmentMakerVisitor lifecyclemv;

				MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, cycleName, desc, null, null);
				lifecyclemv = new FragmentMakerVisitor(
						className,
						Opcodes.ACC_PUBLIC,
						cycleName,
						superClass,
						desc,
						mv);

				lifecyclemv.visitCode();

				// Call onMethodExit()
				if (cycleName.equals("onCreateView")) {
					lifecyclemv.visitInsn(Opcodes.ARETURN);
				} else {
					lifecyclemv.visitInsn(Opcodes.RETURN);
				}
				lifecyclemv.endMethod();
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy