Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac5dc9a4 authored by Yigit Boyar's avatar Yigit Boyar
Browse files

Check API version for methods called via binding

In data binding, setting an attribute actually means calling a method, which might be
an issue if the method is added after a certain API.

This CL introduces a change which will check called methods per api and add necessary
API check code to avoid calling those methods in older platforms.

This CL also resurrects the Java Model Analyzer (in testing) and also fixes compiler tests.

Bug: 19593398
Change-Id: I0da4194625231cf43125e1b43338069e7d191eb9
parent eed3f1fe
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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.
@@ -11,11 +14,13 @@
 * limitations under the License.
 */

package com.android.databinding;
package com.android.databinding.library;

/**
 * Mock class for Observable interface, used for testing.
 * This helper is used to toggle DataBinder's package private values to change behavior for testing
 */
public class MockObservable {

public class DataBinderTrojan {
    public static void setBuildSdkInt(int level) {
        DataBinder.SDK_INT = level;
    }
}
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 * 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.android.databinding.testapp;

import com.android.databinding.library.DataBinderTrojan;
import com.android.databinding.testapp.generated.NewApiLayoutBinder;

import android.content.Context;
import android.os.Build;
import android.test.UiThreadTest;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

public class NewApiTest extends BaseDataBinderTest<NewApiLayoutBinder> {
    public NewApiTest() {
        super(NewApiLayoutBinder.class, R.layout.new_api_layout);
    }

    @UiThreadTest
    public void testSetElevation() {
        mBinder.setElevation(3);
        mBinder.setName("foo");
        mBinder.setChildren(new ArrayList<View>());
        mBinder.rebindDirty();
        assertEquals("foo", mBinder.getTextView().getText().toString());
        assertEquals(3f, mBinder.getTextView().getElevation());
    }

    @UiThreadTest
    public void testSetElevationOlderAPI() {
        DataBinderTrojan.setBuildSdkInt(1);
        try {
            TextView textView = mBinder.getTextView();
            float originalElevation = textView.getElevation();
            mBinder.setElevation(3);
            mBinder.setName("foo2");
            mBinder.rebindDirty();
            assertEquals("foo2", textView.getText().toString());
            assertEquals(originalElevation, textView.getElevation());
        } finally {
            DataBinderTrojan.setBuildSdkInt(Build.VERSION.SDK_INT);
        }
    }

    @UiThreadTest
    public void testGeneric() {
        ArrayList<View> views = new ArrayList<>();
        mBinder.setChildren(views);
        mBinder.rebindDirty();
        assertEquals(1, views.size());
        assertSame(mBinder.getTextView(), views.get(0));
    }

    @UiThreadTest
    public void testGenericOlderApi() {
        DataBinderTrojan.setBuildSdkInt(1);
        try {
            ArrayList<View> views = new ArrayList<>();
            mBinder.setChildren(views);
            mBinder.rebindDirty();
            // we should not call the api on older platforms.
            assertEquals(0, views.size());
        } finally {
            DataBinderTrojan.setBuildSdkInt(Build.VERSION.SDK_INT);
        }
    }
}
+3 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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.
+27 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2015 The Android Open Source Project
  ~ 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.
  -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/myContainer"
              android:addChildrenForAccessibility="@{children}"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <variable name="elevation" type="float"/>
    <variable name="name" type="java.lang.String"/>
    <variable name="children" type="java.util.ArrayList&lt;android.view.View>"/>
    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:id="@+id/textView"
              android:text="@{name}" android:elevation="@{elevation}"/>
</LinearLayout>
 No newline at end of file
+20 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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.android.databinding.annotationprocessor;

import com.android.databinding.CompilerChef;
import com.android.databinding.reflection.SdkUtil;
import com.android.databinding.store.ResourceBundle;
import com.android.databinding.util.L;
import com.android.databinding.writer.AnnotationJavaFileWriter;

import org.apache.commons.codec.binary.Base64;
@@ -11,6 +29,7 @@ import android.binding.BinderBundle;
import android.binding.BindingAppInfo;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
@@ -37,12 +56,12 @@ public class ProcessExpressions extends AbstractProcessor {

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        ResourceBundle resourceBundle = null;

        for (Element element : roundEnv.getElementsAnnotatedWith(BindingAppInfo.class)) {
            final BindingAppInfo appInfo = element.getAnnotation(BindingAppInfo.class);
            if (appInfo == null) {
                continue; // It gets confused between BindingAppInfo and BinderBundle
            }
            SdkUtil.initialize(appInfo.minSdk(), new File(appInfo.sdkRoot()));
            if (element.getKind() != ElementKind.CLASS) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                        "BindingAppInfo associated with wrong type. Should be a class.", element);
Loading