[] Dissect layout traversals on Android

Related tags

Tools probe
Overview

Probe

Dissect layout traversals on Android.

Features

  • Intercept View methods.
    • onMeasure(int, int)
    • onLayout(boolean, int, int, int, int)
    • draw(Canvas) and onDraw(Canvas)
    • requestLayout()
  • Override any of these methods on-the-fly.
  • Layout debugging:
    • OvermeasureInterceptor: Tints views according to the number of times they got measured in a single traversal.
    • LayoutBoundsInterceptor: Equivalent to Android's "Show layout bounds" developer option. The main difference being that you can show bounds only for specific views.

Usage

  1. Add buildscript dependency:
buildscript {
    ...
    dependencies {
        ...
        classpath 'org.lucasr.probe:gradle-plugin:0.1.3'
    }
}
  1. Apply the plugin to your app’s build.gradle:
apply plugin: 'org.lucasr.probe'
  1. Enable Probe on a build variant:
probe {
    buildVariants {
        debug {
            enabled = true
        }
    }
}
  1. Implement an Interceptor:
public class DrawGreen extends Interceptor {
    private final Paint mPaint;

    public DrawGreen() {
        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);
    }

    @Override
    public void onDraw(View view, Canvas canvas) {
        canvas.drawPaint(mPaint);
    }
}
  1. Deploy your Interceptor with an (optional) Filter:
public final class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Probe.deploy(this, new DrawGreen(), new Filter.ViewId(R.id.view2));
        super.onCreate(savedInstanceState);
        setContentView(R.id.main_activity);
    }
}

Download

Download the latest JAR or grab via Gradle:

compile 'org.lucasr.probe:probe:0.1.3'

or Maven:

<dependency>
  <groupId>org.lucasr.probe</groupId>
  <artifactId>probe</artifactId>
  <version>0.1.3</version>
</dependency>

License

Copyright 2014 Lucas Rocha

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.
Comments
  • <fragment> tag throws ClassCastException

    tag throws ClassCastException

    The fragment in use is the support library version. But the LayoutInflater tries to inflate the android version (android.app.fragment), and tries to cast to it. This causes a ClassNotFoundException. The same piece of code works fine without Probe.

    E/AndroidRuntime( 1156): at android.app.Fragment.instantiate(Fragment.java:607) E/AndroidRuntime( 1156): at android.app.Fragment.instantiate(Fragment.java:583) E/AndroidRuntime( 1156): at android.app.Activity.onCreateView(Activity.java:4859) E/AndroidRuntime( 1156): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:689) E/AndroidRuntime( 1156): ... 23 more E/AndroidRuntime( 1156): Caused by: java.lang.ClassCastException E/AndroidRuntime( 1156): ... 27 more

    opened by sriramramani 7
  • Package name in generated code is null

    Package name in generated code is null

    I ran into another problem -- the package name for the generated source files is null.probe, which is invalid. Where's it supposed to get the package name from?

    By the way, how do I run / debug Probe itself? I tried to set a breakpoint in ProbeTask (to see where it's getting the package name) and then ran the sample project, but no dice.

    Example: (this happens for every view class)

    package null.probe;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import org.lucasr.probe.InterceptableView;
    import org.lucasr.probe.Interceptor;
    import android.view.View;
    public final class ProbeProxy$android_view_View extends View
        implements InterceptableView {
      private Interceptor mInterceptor;
      public ProbeProxy$android_view_View(Context context, AttributeSet attrs) {
        super(context, attrs);
      }
      public void setInterceptor(Interceptor interceptor) {
        mInterceptor = interceptor;
      }
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mInterceptor != null) {
          mInterceptor.onMeasure(this, widthMeasureSpec, heightMeasureSpec);
        }
        else {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
      }
      public void superOnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      }
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (mInterceptor != null) {
          mInterceptor.onLayout(this, changed, left, top, right, bottom);
        }
        else {
          super.onLayout(changed, left, top, right, bottom);
        }
      }
      public void superOnLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
      }
      public void draw(Canvas canvas) {
        if (mInterceptor != null) {
          mInterceptor.draw(this, canvas);
        }
        else {
          super.draw(canvas);
        }
      }
      public void superDraw(Canvas canvas) {
        super.draw(canvas);
      }
      protected void onDraw(Canvas canvas) {
        if (mInterceptor != null) {
          mInterceptor.onDraw(this, canvas);
        }
        else {
          super.onDraw(canvas);
        }
      }
      public void superOnDraw(Canvas canvas) {
        super.onDraw(canvas);
      }
      public void requestLayout() {
        if (mInterceptor != null) {
          mInterceptor.requestLayout(this);
        }
        else {
          super.requestLayout();
        }
      }
      public void superRequestLayout() {
        super.requestLayout();
      }
      public void superSetMeasuredDimension(int width, int height) {
        super.setMeasuredDimension(width, height);
      }
    }
    
    opened by bishopmatthew 4
  • Can't build a project

    Can't build a project

    Hey, I'm trying to add Probe to my project and get this error: /Users/Zaur/Projects/android/MyListView/app/build/generated/source/probe/debug/ru/agamov/mylistview/probe/ProbeProxy$android_widget_DateTimeView.java Error:(8, 22) error: cannot find symbol class DateTimeView Error:(9, 67) error: cannot find symbol class DateTimeView Error:(20, 19) error: method onMeasure in class Interceptor cannot be applied to given types; required: View,int,int found: ProbeProxy$android_widget_DateTimeView,int,int reason: actual argument ProbeProxy$android_widget_DateTimeView cannot be converted to View by method invocation conversion

    Here is my build.gradle:

    apply plugin: 'com.android.application'
    apply plugin: 'org.lucasr.probe'
    
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'org.lucasr.probe:gradle-plugin:0.1.3'
        }
    }
    
    android {
        compileSdkVersion 22
        buildToolsVersion "22.0.1"
    
        defaultConfig {
            applicationId "ru.agamov.mylistview"
            minSdkVersion 14
            targetSdkVersion 22
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
    
            debug {
                minifyEnabled false
            }
        }
    }
    
    probe {
        buildVariants {
            debug {
                enabled = true
            }
        }
    }
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.2.0'
    }
    
    opened by zaur 3
  • Final View classes throw error

    Final View classes throw error

    E/AndroidRuntime(20649): java.lang.IncompatibleClassChangeError: superclass is final E/AndroidRuntime(20649): at dalvik.system.DexFile.defineClassNative(Native Method) E/AndroidRuntime(20649): at dalvik.system.DexFile.defineClass(DexFile.java:222) E/AndroidRuntime(20649): at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:215) E/AndroidRuntime(20649): at dalvik.system.DexPathList.findClass(DexPathList.java:322) E/AndroidRuntime(20649): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:54) E/AndroidRuntime(20649): at java.lang.ClassLoader.loadClass(ClassLoader.java:497) E/AndroidRuntime(20649): at java.lang.ClassLoader.loadClass(ClassLoader.java:457) E/AndroidRuntime(20649): at org.lucasr.probe.ViewProxyBuilder.generateProxyClass(ViewProxyBuilder.java:428) E/AndroidRuntime(20649): at org.lucasr.probe.ViewProxyBuilder.build(ViewProxyBuilder.java:559) E/AndroidRuntime(20649): at org.lucasr.probe.ProbeViewFactory.createProxyView(ProbeViewFactory.java:51) E/AndroidRuntime(20649): at org.lucasr.probe.ProbeViewFactory.onCreateView(ProbeViewFactory.java:72) E/AndroidRuntime(20649): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:684) E/AndroidRuntime(20649): at android.view.LayoutInflater.rInflate(LayoutInflater.java:755) E/AndroidRuntime(20649): at android.view.LayoutInflater.inflate(LayoutInflater.java:492) E/AndroidRuntime(20649): at android.view.LayoutInflater.inflate(LayoutInflater.java:397) E/AndroidRuntime(20649): at android.view.LayoutInflater.inflate(LayoutInflater.java:353) E/AndroidRuntime(20649): at com.android.internal.policy.impl.PhoneWindow.generateLayout(PhoneWindow.java:3022) E/AndroidRuntime(20649): at com.android.internal.policy.impl.PhoneWindow.installDecor(PhoneWindow.java:3085) E/AndroidRuntime(20649): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:286)

    opened by sriramramani 3
  • groovy.lang.MissingMethodException

    groovy.lang.MissingMethodException

    Hi Lucas,

    When I add probe as a dependency to my app, apply the plugin, and enable it for my debug builds, compilation fails with this exception, even before I deploy Probe in any of my Activities. Any idea what's going on? Happy to provide any more information you need.

    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':probeDebugViews'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
        at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:305)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
        at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
        at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
    Caused by: groovy.lang.MissingMethodException: No signature of method: org.lucasr.probe.internal.LayoutResourceParser$_traverseLayoutXml_closure1.traverseLayoutXml() is applicable for argument types: (java.lang.String, java.util.HashSet) values: [
    
    
    
    
    
    
    
    
                />
    
    
    , ...]
        at org.lucasr.probe.internal.LayoutResourceParser$_traverseLayoutXml_closure1.doCall(LayoutResourceParser.groovy:45)
        at org.lucasr.probe.internal.LayoutResourceParser.traverseLayoutXml(LayoutResourceParser.groovy:45)
        at org.lucasr.probe.internal.LayoutResourceParser.parse(LayoutResourceParser.groovy:29)
        at org.lucasr.probe.internal.LayoutResourceParser$parse.call(Unknown Source)
        at org.lucasr.probe.ProbeTask$_parseLayoutFiles_closure3.doCall(ProbeTask.groovy:82)
        at org.lucasr.probe.ProbeTask.parseLayoutFiles(ProbeTask.groovy:81)
        at org.lucasr.probe.ProbeTask.this$4$parseLayoutFiles(ProbeTask.groovy)
        at org.lucasr.probe.ProbeTask$this$4$parseLayoutFiles.callCurrent(Unknown Source)
        at org.lucasr.probe.ProbeTask.taskAction(ProbeTask.groovy:51)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.doExecute(AnnotationProcessingTaskFactory.java:235)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:211)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.execute(AnnotationProcessingTaskFactory.java:222)
        at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:200)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
        ... 13 more
    
    
    BUILD FAILED
    
    opened by bishopmatthew 2
  • Chain Interceptors

    Chain Interceptors

    This is not a real issues but more a question: Is it possible to chain Interceptors? If I deploy multiple Interceptors like this:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Probe.deploy(this, new OvermeasureInterceptor(R.id.root));
        Probe.deploy(this, new LayoutBoundsInterceptor(this));
        Probe.deploy(this, new ImageViewInterceptor(this));
    

    I get a Runtime Exception:

    java.lang.IllegalStateException
                               : A factory has already been set on this LayoutInflater
    opened by dhelleberg 2
  • Need for ProbeLayoutInflater

    Need for ProbeLayoutInflater

    If there is a ListView, the rows in the ListView have a different inflater. It's hard to create probe for each inflation inside each Adapter. It would be better to have a ProbeLayoutInflater that is returned by the MainActivity. That way, everything inside an Activity can be probed.

    opened by sriramramani 2
  • ProbeViewFactory does not implement both the onCreateView methods

    ProbeViewFactory does not implement both the onCreateView methods

    If a setFactory2() is already provided using fragments, Android does a merge of the factories. In that case, it may call onCreateView() without the parent argument. In this case Probe will not be created, hence no probing will be done.

    opened by sriramramani 1
  • View in XML is throwing errors

    View in XML is throwing errors

    in XML throws the following error.

    W/System.err(18079): java.lang.ClassNotFoundException: Couldn't load View class for View W/System.err(18079): at org.lucasr.probe.ViewClassUtil.findViewClass(ViewClassUtil.java:67) W/System.err(18079): at org.lucasr.probe.ProbeViewFactory.createProxyView(ProbeViewFactory.java:51) W/System.err(18079): at org.lucasr.probe.ProbeViewFactory.onCreateView(ProbeViewFactory.java:72) W/System.err(18079): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:684) W/System.err(18079): at android.view.LayoutInflater.rInflate(LayoutInflater.java:755) W/System.err(18079): at android.view.LayoutInflater.inflate(LayoutInflater.java:492) W/System.err(18079): at android.view.LayoutInflater.inflate(LayoutInflater.java:397) W/System.err(18079): at android.view.LayoutInflater.inflate(LayoutInflater.java:353)

    opened by sriramramani 0
  • After fresh pull from git, does not compile.

    After fresh pull from git, does not compile.

    I pulled fresh from git to fork, but when I tried to compile, it gave this error:

    probe/library/src/main/java/org/lucasr/probe/DexProxyBuilder.java:27: error: package com.google.dexmaker does not exist
    import com.google.dexmaker.Code;
    

    This repeats for all the imported classes in DexProxyBuilder.

    opened by ihowell 0
  • error: cannot find symbol class DateTimeView

    error: cannot find symbol class DateTimeView

    Hi, I'm having this strange error show up. I've put your DrawGreen sample class into a new sample project, and am calling

    Probe.deploy(this, new DrawGreen(), new Filter.ViewId(R.id.start_button));

    on the onCreate of my Activity, just before the super.onCreate(). R.id.start_button is just the Button class.

    I dug a little and it seems like this class is getting generated:

    public final class ProbeProxy$android_widget_DateTimeView extends DateTimeView
        implements InterceptableView {
    ...
    }
    

    But it looks like it's unable to find DateTimeView.

    I'm on Android Studio 2.1.1, compileSdkVersion 23.

    opened by waynesford 2
  • Can't enable Probe in build.gradle

    Can't enable Probe in build.gradle

    I've added this to my build.gradle file; but when I try running assembleProbeDebug in the gradle logs I see that probe is disabled; when I run my code it is in fact disabled.

    productFlavors { probe { buildVariants { debug { enabled true } } }

    }
    
    opened by spark85 1
  • Various fixes to get it running in my project

    Various fixes to get it running in my project

    See https://github.com/lucasr/probe/issues/14 for more discussion. I also added the gradle tasks installLibrary and installPlugin to get it into my local repo more easily for testing.

    I tried to follow your coding / commit message style, but I'm happy to make any changes you want.

    opened by bishopmatthew 0
Owner
Lucas Rocha
Mobile UI platform at Facebook. Formerly at Mozilla, GNOME, litl, and Nokia.
Lucas Rocha
A tool to install components of the Android SDK into a Maven repository or repository manager to use with the Android Maven Plugin, Gradle and other tools.

Maven Android SDK Deployer Original author including numerous fixes and changes: Manfred Moser [email protected] at simpligility technologies i

simpligility 1.4k Dec 27, 2022
A simple utility to remove unused resources in your Android app to lower the size of the APK. It's based on the Android lint tool output.

android-resource-remover android-resource-remover is utility that removes unused resources reported by Android Lint from your project. The goal is to

Keepsafe 1.3k Dec 16, 2022
This is a Android Studio/ IntelliJ IDEA plugin to localize your Android app, translate your string resources automactically.

#Android Localizationer This is a Android Studio/ IntelliJ IDEA plugin to localize your Android app, translate your string resources automactically. T

Wesley Lin 822 Dec 8, 2022
A tool to install components of the Android SDK into a Maven repository or repository manager to use with the Android Maven Plugin, Gradle and other tools.

Maven Android SDK Deployer Original author including numerous fixes and changes: Manfred Moser [email protected] at simpligility technologies i

simpligility 1.4k Dec 27, 2022
Automated-build-android-app-with-github-action - CI/CD Automated Build Android App Bundle / APK / Signed With Github Action

Automated Build Android With Using Github Action Project Github Action Script Us

Faisal Amir 34 Dec 19, 2022
proguard resource for Android by wechat team

AndResGuard Read this in other languages: English, 简体中文. AndResGuard is a tooling for reducing your apk size, it works like the ProGuard for Java sour

shwenzhang 8.1k Jan 9, 2023
A super fast build tool for Android, an alternative to Instant Run

Freeline Freeline is a super fast build tool for Android and an alternative to Instant Run. Caching reusable class files and resource indices, it enab

Alibaba 5.5k Jan 2, 2023
Command-line tool to count per-package methods in Android .dex files

dex-method-counts Simple tool to output per-package method counts in an Android DEX executable grouped by package, to aid in getting under the 65,536

Mihai Parparita 2.6k Nov 25, 2022
View Inspection Toolbar for Android Development

View Inspector Plugin View inspection toolbar for android development. Features Boundary show outlines show margins show paddings Layer Scalpel featur

Fumihiro Xue (Peter Hsieh) 2.2k Nov 14, 2022
Make Android screenshots of scrollable screen content

scrollscreenshot Make Android screenshots of scrollable screen content - brought to you by PGS Software SA This tool makes a number of screenshots, sc

PGS Software 714 Dec 7, 2022
🍼Debug Bottle is an Android runtime debug / develop tools written using kotlin language.

???? 中文 / ???? 日本語 / ???? English ?? Debug Bottle An Android debug / develop tools written using Kotlin language. All the features in Debug bottle are

Yuriel Arlencloyn 846 Nov 14, 2022
Android Library Finder

alfi Android Library Finder Search through thousands of android libraries that can help you scale your projects elegantly Usage Search for something a

César Ferreira 509 Dec 8, 2022
Annotation based simple API flavored with AOP to handle new Android runtime permission model

Let Annotation based simple API flavoured with AOP to handle new Android runtime permission model. If you check Google's Samples about the new permiss

Can Elmas 530 Nov 25, 2022
Combines tools for fast android app devlopment

Android - Rapid Test Driven Development Combine tools to generate most of the boilerplate code. Examples how to test different aspects of an android a

Nico Küchler 379 Nov 25, 2022
Make mosaic effect on android

ProMosaic Make mosaic for image on android. Features Select Mode Follow finger Select rectangle Effect Mode Grid color based on original image Blur Im

dawson 359 Dec 29, 2022
A set of Android tools that facilitate apps development

A set of Android tools that facilitate apps development Well, this repo contains pretty much code used internally at Stanfy to develop Android apps. S

Stanfy 183 Dec 3, 2022
Remote script to create a maven compatible release of an android library (aar)

release-android-library ?? Deprecated ?? This script is deprecated in favour of: novoda/bintray-release Remote script to create a maven compatible rel

Paul Blundell 144 Dec 13, 2022