How to specify different buildTypes in Gradle 3.0.0 beta 4 and later

After updating to Gradle plugin 3.0.0 beta 4 our build failed with the following message:

buildTypeMatching has been removed. Use buildTypes..fallbacks

Our libraries have release and debug buildTypes, but our app has two additional buildTypes: releaseWithLogs and debugMinified.

Snippet of our app Gradle file:

android {
    // ...
    buildTypeMatching 'releaseWithLogs', 'release'
    buildTypeMatching 'debugMinified', 'debug'

    buildTypes {
        debug {
            // ...
        }
        debugMinified {
            // ...
        }
        release {
            // ...
        }
        releaseWithLogs {
            // ...
        }
    }
}

After some investigation, the following announcement has been found: Android Studio 3.0 Beta 4 is now available. There, it mentions:

You now provide fallbacks for missing build types and flavors using matchingFallbacks (which replaces buildTypeMatching and productFlavorMatching). You also provide the default selection and fallbacks for missing dimensions using missingDimensionStrategy (which replaces flavorSelection).

So, our previous app build.gradle gets converted to:

android {
    // ...
    //buildTypeMatching 'releaseWithLogs', 'release' // remove this
    //buildTypeMatching 'debugMinified', 'debug'     // remove this

    buildTypes {
        debug {
            // ...
        }
        debugMinified {
            // ...
            matchingFallbacks = ['debug']    // instead use this
        }
        release {
            // ...
        }
        releaseWithLogs {
            // ...
            matchingFallbacks = ['release']  // instead use this
        }
    }
}

Notice that, instead of saying that buildType releaseWithLogs will also match with release (buildTypeMatching 'releaseWithLogs', 'release'), we specify the match inside the buildType itself. Same for debugMinified matching debug. Also notice that there’s no need to include this in release and debug buildTypes, as they already match.

The original question and answer can be found in Gradle plugin 3.0.0 beta 4: “buildTypeMatching has been removed. Use buildTypes..fallbacks”

Kotlin interop: mixing Kotlin and Java ButterKnife-annotated activities

I’ve been working with Kotlin for a while, mainly for side-projects or toy-projects. Since last Google I/O 2017 announcement it has become clear that there are no more reasons or excuses to not use it in production.

One of the big selling points of Kotlin is that you can start small, by converting one class or two, or by creating new ones, while keeping all the remaining code in Java. So, interop between the two languages is almost 100% transparent. Almost.

Working to convert a small project step by step, I started to convert activities into Kotlin. Those activities use ButterKnife (I’m using current version, which is 8.7.0) to inject the views. So, after converting the first activity I stumbled upon a problem with the annotation processor: in Gradle script, you have to use either annotationProcessor or kapt, but not both at the same time. So, you have to choose:

  • using annotationProcessor only will not find Kotlin classes, and because of that injection will silently fail at runtime,
  • using kapt only will make compilation fail.

The final workaround I found was:

  • using kapt3 (by applying kotlin-kapt plugin to the Gradle script) and,
  • adding a @JvmField() annotation in addition to ButterKnife annotations so Kotlin compiler generates public fields instead of getters and setters.

By applying kapt3 we fix the compilation error involving “kotlin.jvm.internal.FunctionReference.(ILjava/lang/Object;)V” and by converting Kotlin fields to plain-old Java fields we allow ButterKnife compiler to find the fields to inject, as is unable to find Kotlin fields.

You can find the source code with different options in different branches (the one with the final solution is kotlin-workaround) in this GitHub project.

The project has two activities, one (MainActivity) that is written in Java and kept in this language, and the second one (NextActivity) that is converted to Kotlin. Notice that a simple suite of tests is available to check that both activities are being correctly injected, and that there is a TextView in both activities that has its text replaced by code to prove that the activity has been successfully injected.

Hope this tip is useful!

Disabling (and removing) code on release builds

Repo updated on 2017-03-13: added additional debug configurations to check different options to enable/disable minification, optimization and obfuscation. See the discussion in Optimize without obfuscate your debug build, including this comment.

Sometimes you have to add code to your applications that is used for debugging purposes. This can be very useful, and sometimes is keep there as it helps in the development and debugging of different parts of the application. But, some of this code can have unintended consequences:

  • it can reveal sensitive data to a potential attacker (internal URLs, session cookies, etc.)
  • it can have a performance impact in your application (excessive logging, performing operations not needed for release builds, etc.)
  • it can lower the security of your application (backdoor-like features to help while debugging, that can disable certain security features, or completely bypass them, etc.)

Continue reading Disabling (and removing) code on release builds

Jenkins builds with a different Java version

We have a Jenkins server taking care of CI for an Android project. In the server we were using Java 7, but since we updated a few dependencies we needed Java 8 to run some Gradle plugins. After the change, we suddenly started to get this error in the builds:

* What went wrong:
A problem occurred evaluating project ':my-project-app'.
> java.lang.UnsupportedClassVersionError: com/android/build/gradle/AppPlugin : Unsupported major.minor version 52.0

After googling it, I found the following SO answer, pointing us to the need of Java 8 (major version 52): How to fix java.lang.UnsupportedClassVersionError: Unsupported major.minor version.

So, given that:

  1. server has both Java 7 and Java 8 installed
  2. Jenkins is configured to run using Java 7, and changing may pose some problems (and currently any failure could put our schedule in jeopardy)

we decided to just run Gradle script using Java 8. To configure it, the following change was done to the configuration of the job: in section Build Environment, enable Inject environment variables to the build process and add the following to Properties Content:

JAVA_HOME=(path_to_jdk)

in our case:

JAVA_HOME=/usr/lib/jvm/java-8-oracle

And hit Rebuild!

Problems with Instant Run (Android Studio 2.0 beta 4) and Retrofit

This morning I was playing with a toy app I have which uses Retrofit, and I’ve found the following problem with it (and Instant Run):

02-16 07:41:55.550 2976-2976/com.test.android A/art: art/runtime/thread.cc:1329] Throwing new exception 'length=227; index=1017' with unexpected pending exception: java.lang.ArrayIndexOutOfBoundsException: length=227; index=1017

The exception was in the call itself, so it was either Retrofit doing strange things or something deeper. I found the problem to be because of some interaction between ART and Instant Run, and it was already reported.

The original reporter has opened a bug in the AOSP and it’s still unsolved. See also (Retrofit).

All in all, currently there is no fully reliable solution. So, if you find this problem in a call using Retrofit, the best course of action is to disable Instant Run and rebuild. Preferences -> Build, Execution, Deployment -> Instant Run -> (uncheck) Enable Instant Run to hot swap code/resource changes on deploy (default enabled)

Disable Instant Run

Kotlin RC and kotlinx

I’ve been toying with Kotlin for a while. Yesterday RC was released, making it one step closer to have a stable version.

One small change that has made me scratch my head a little, even if I read the solution, has been the changes regarding Kotlin Extensions. This had been integrated with the main Kotlin plugin (instead of being a separated plugin) but on RC you need to change your Gradle file a little to keep using it.

The symptom is that you find an “Unresolved reference: kotlinx” plus others that come from the same namespace. Resolution is easy: in the announcement for the release Kotlin 1.0 Release Candidate is Out! in the section Tooling, it reads (bold is mine):

To enable Android Extensions in Gradle in a more idiomatic way, we now say:

apply plugin: 'kotlin-android-extensions'

in the build.gradle file (individually for each project).

The old way doesn’t work any more and prints fixing instructions to the output.

So, I haven’t found this instructions, but the solution is easy: apply the new kotlin-android-extensions plugin right after the kotlin-android plugin in your gradle file. Usually, the end result will be like this:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

After that you just need to sync your Gradle configuration and rebuild the whole project.

In-depth article on Android touch events handling

Sometimes you need to intercept touches for a given ViewGroup, to temporarily change or disable it’s behavior. For example, recently I had to do this to temporarily intercept events for a RecyclerView during a tutorial, instructing the user how to select a particular item.

In this case, I could simply add an overlaid Layout on top of everything, but to keep as close to the real thing as possible, I decided to intercept the touch events at the RecyclerView level. The problem is, how to detect if the touch event needs to be intercepted or not. Two possible solutions came to my mind:

  • Intercept (or somehow disable) the events on each individual item, except on the one we’re interested on receiving it. For me it’s a bit messy to have this logic scattered.
  • Intercept the events on the RecyclerView, and check if the event belongs to the child, to allow further processing.

So, I decided to go for the second solution. To do that, I investigated a bit more in depth how touch events are handled in Android. This a really interesting article on how this happens: Understanding Android Input Touch Events System Framework (dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent, OnTouchListener.onTouch). Also, it is really useful to see the real code on how a ViewGroup handles this. Even if I could download Android source code from the Android Open Source Project web site, I didn’t really felt like it. So I found this copy online.

The interesting part for us is ViewGroup#dispatchTouchEvent(MotionEvent), and specifically in the loop where it iterates all the child and checks if the touch event is within the bounds of this child.

Based on this, I extracted the relevant code, which boils down to retrieving the hit rectangle of the child we want to allow processing the event, and checking it against the touch target. This class has to be overridden and the getAllowedChildView() method implemented, returning the child we’re interested in allowing the touch events go through.

public class CustomRecyclerView extends RecyclerView {

    private static final String LOG_MARKER = CustomRecyclerView.class.getName();

    private boolean scrollEnabled = true;

    public CustomRecyclerView(Context context) {
        super(context);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setEnabledRecycleScroll(final boolean enable) {

        if (scrollEnabled == enable) {
            Logger.getInstance().debug(LOG_MARKER, "RecyclerView scrolling is already " +
                    (enable ? "enabled" : "disabled") + ", skipping");
            return;
        }

        scrollEnabled = enable;

        if (!enable) {
            Logger.getInstance().debug(LOG_MARKER, "Disabling RecyclerView scrolling");
            addOnItemTouchListener(disablerListener); // disables scrolling
        } else {
            Logger.getInstance().debug(LOG_MARKER, "Enabling RecyclerView scrolling");
            removeOnItemTouchListener(disablerListener); // scrolling is enabled again
        }

    }

    // Override this method to return the child we're interested in allowing touch events
    // to go through
    abstract protected View getAllowedChildView();

    private RecyclerView.OnItemTouchListener disablerListener =
            new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {

            // Inspired by ViewGroup#dispatchTouchEvent(MotionEvent). See
            // http://codetheory.in/understanding-android-input-touch-events/ and
            // http://www.netmite.com/android/mydroid/frameworks/base/core/java/android/view/ViewGroup.java
            final int action = ev.getAction();
            if (action == MotionEvent.ACTION_DOWN) {
                final float scrolledX = ev.getX() + CustomRecyclerView.this.getScrollX();
                final float scrolledY = ev.getY() + CustomRecyclerView.this.getScrollY();
                final Rect frame = new Rect();
                View allowedChildView = getAllowedChildView();
                if (allowedChildView != null) {
                    allowedChildView.getHitRect(frame);
                    if (frame.contains((int) scrolledX, (int) scrolledY)) {
                        // Do not intercept the touch events for this child
                        return false;
                    }
                }
            }

            return true;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        }
    };

}