Java tutorial
/* * Copyright (c) 2014. WillowTree Apps * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.willowtreeapps.respeeker; import android.app.Activity; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import java.io.IOException; import java.io.StringWriter; import java.util.Properties; @Aspect public class ResPeeker { @Pointcut("target(android.app.Activity) && call(void setContentView(int))") public void setContentView() { } @Pointcut("target(android.view.View) && call(void setOnTouchListener(android.view.View$OnTouchListener))") public void setOnTouchListener() { } @Pointcut("target(android.view.LayoutInflater) && call(android.view.View inflate(int, android.view.ViewGroup, boolean))") public void inflate() { } @Around("setContentView()") public Object aroundSetContentView(ProceedingJoinPoint joinPoint) throws Throwable { Object result = joinPoint.proceed(); Activity context = (Activity) joinPoint.getThis(); int layoutRes = (Integer) joinPoint.getArgs()[0]; View view = context.findViewById(android.R.id.content); attachTouchListener(view); ViewPeeker.put(view, layoutRes); Configurator configurator = new Configurator(context); Properties properties = configurator.getProperties(); Log.v("__ResPeeker__", "config <<" + propertiesToString(properties) + ">>"); return result; } @Around("inflate()") public Object aroundInflate(ProceedingJoinPoint joinPoint) throws Throwable { Object result = joinPoint.proceed(); int layoutRes = (Integer) joinPoint.getArgs()[0]; View layoutView = (View) joinPoint.getArgs()[1]; View view = (View) result; attachTouchListener(view); ViewPeeker.put(layoutView, layoutRes); return result; } @Around("setOnTouchListener()") public Object aroundSetOnTouchListener(ProceedingJoinPoint joinPoint) throws Throwable { final View.OnTouchListener originalTouchListener = (View.OnTouchListener) joinPoint.getArgs()[0]; final View.OnTouchListener interceptTouchListener = getTouchListener(); View.OnTouchListener delegateTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { interceptTouchListener.onTouch(v, event); return originalTouchListener != null && originalTouchListener.onTouch(v, event); } }; return joinPoint.proceed(new Object[] { delegateTouchListener }); } public void attachTouchListener(View view) { view.setOnTouchListener(null); if (view instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) view; for (int i = 0; i < viewGroup.getChildCount(); i++) { attachTouchListener(viewGroup.getChildAt(i)); } } } public View.OnTouchListener getTouchListener() { return new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { int layoutRes = ViewPeeker.getLayout(v); if (layoutRes > 0) { String layoutName = v.getContext().getResources().getResourceName(layoutRes); Log.v("__ResPeeker__", "layout <<" + layoutName + ">>"); } } return false; } }; } public String propertiesToString(Properties properties) { StringWriter writer = new StringWriter(); try { properties.store(writer, null); } catch (IOException e) { // Should never happen since StringWriter doesn't do IO. throw new RuntimeException(e); } return writer.toString(); } // This shouldn't be required? public static ResPeeker aspectOf() { return new ResPeeker(); } }