diff --git a/README.md b/README.md index b650946..2dad7f7 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,7 @@ buildscript { } dependencies { - compile 'com.github.aakira:expandable-layout:1.4.2@aar' + compile 'com.github.aakira:expandable-layout:1.4.3@aar' } ``` diff --git a/build.gradle b/build.gradle index daa3645..95fd671 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:1.3.1' classpath 'com.novoda:bintray-release:0.3.4' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION" } } diff --git a/circle.yml b/circle.yml index 47f31cd..b28cfbc 100644 --- a/circle.yml +++ b/circle.yml @@ -13,11 +13,15 @@ dependencies: test: pre: - - emulator -avd circleci-android21 -no-skin -no-audio -no-window: + - emulator -avd circleci-android21 -no-audio -no-window: background: true parallel: true - circle-android wait-for-boot override: + # unlock the emulator screen + - sleep 30 + - adb shell input keyevent 82 - ./gradlew connectedAndroidTest -PdisablePreDex post: - cp -R ./library/build/reports/androidTests/connected/* $CIRCLE_ARTIFACTS + - cp -R ./library-ui-test/build/reports/androidTests/connected/* $CIRCLE_ARTIFACTS \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index bb64e9c..17ae2c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,10 +2,12 @@ COMPILE_SDK_VERSION=23 BUILD_TOOLS_VERSION=22.0.1 MIN_SDK_VERSION=11 TARGET_SDK_VERSION=23 -VERSION_CODE=7 -VERSION_NAME=1.4.2 +VERSION_CODE=8 +VERSION_NAME=1.4.3 SUPPORT_TEST_VERSION=0.4.1 HAMCREST_VERSION=1.3 +ESPRESSO_VERSION=2.2.1 +KOTLIN_VERSION=1.0.0 SUPPORT_APP_COMPAT_VERSION=23.0.1 diff --git a/library-ui-test/.gitignore b/library-ui-test/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/library-ui-test/.gitignore @@ -0,0 +1 @@ +/build diff --git a/library-ui-test/build.gradle b/library-ui-test/build.gradle new file mode 100644 index 0000000..0688c67 --- /dev/null +++ b/library-ui-test/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion COMPILE_SDK_VERSION as int + buildToolsVersion BUILD_TOOLS_VERSION + + defaultConfig { + minSdkVersion MIN_SDK_VERSION as int + targetSdkVersion TARGET_SDK_VERSION as int + versionCode VERSION_CODE as int + versionName VERSION_NAME + + testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + compile project(':library') + + compile "com.android.support:appcompat-v7:$SUPPORT_APP_COMPAT_VERSION" + compile "org.jetbrains.kotlin:kotlin-stdlib:$KOTLIN_VERSION" + androidTestCompile "com.android.support:support-annotations:$SUPPORT_APP_COMPAT_VERSION" + androidTestCompile "com.android.support.test:runner:$SUPPORT_TEST_VERSION" + androidTestCompile "com.android.support.test:rules:$SUPPORT_TEST_VERSION" + androidTestCompile "com.android.support.test.espresso:espresso-core:$ESPRESSO_VERSION" +} \ No newline at end of file diff --git a/library-ui-test/proguard-rules.pro b/library-ui-test/proguard-rules.pro new file mode 100644 index 0000000..82e73a8 --- /dev/null +++ b/library-ui-test/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Applications/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivityTest.kt b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivityTest.kt new file mode 100644 index 0000000..9d9f113 --- /dev/null +++ b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivityTest.kt @@ -0,0 +1,107 @@ +package com.github.aakira.expandablelayout.uitest + +import android.app.Activity +import android.support.test.InstrumentationRegistry +import android.support.test.espresso.Espresso +import android.support.test.espresso.Espresso.onView +import android.support.test.espresso.assertion.ViewAssertions.matches +import android.support.test.espresso.matcher.ViewMatchers.withId +import android.support.test.runner.AndroidJUnit4 +import android.test.ActivityInstrumentationTestCase2 +import com.github.aakira.expandablelayout.ExpandableRelativeLayout +import com.github.aakira.expandablelayout.uitest.utils.ElapsedIdLingResource +import com.github.aakira.expandablelayout.uitest.utils.equalHeight +import com.github.aakira.expandablelayout.uitest.utils.orMoreHeight +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.IsNull.notNullValue +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.hamcrest.CoreMatchers.`is` as _is + +@RunWith(AndroidJUnit4::class) +class ExpandableRelativeLayoutActivityTest : ActivityInstrumentationTestCase2 +(ExpandableRelativeLayoutActivity::class.java) { + + companion object { + val DURATION = 500L + } + + @Before + @Throws(Exception::class) + public override fun setUp() { + super.setUp() + injectInstrumentation(InstrumentationRegistry.getInstrumentation()) + } + + @After + @Throws(Exception::class) + public override fun tearDown() { + super.tearDown() + } + + @Test + fun testExpandableRelativeLayout() { + val activity = activity + val instrumentation = instrumentation + + // check activity + assertThat(activity, notNullValue()) + assertThat(instrumentation, notNullValue()) + + val expandableLayout = activity.findViewById(R.id.expandableLayout) as ExpandableRelativeLayout + + // default close + onView(withId(R.id.expandableLayout)).check(matches(equalHeight(0))) + + // open toggle + instrumentation.runOnMainSync { expandableLayout.toggle() } + var idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + onView(withId(R.id.expandableLayout)).check(matches(orMoreHeight(1))) + Espresso.unregisterIdlingResources(idlingResource) + + // move to first layout + instrumentation.runOnMainSync { expandableLayout.moveChild(0) } + idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + onView(withId(R.id.child1)).check(matches(equalHeight(expandableLayout))) + Espresso.unregisterIdlingResources(idlingResource) + + // set close height + instrumentation.runOnMainSync { expandableLayout.closePosition = expandableLayout.currentPosition; } + + // move to second layout + instrumentation.runOnMainSync { expandableLayout.moveChild(1) } + idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + // second.height != 0 && first.height + second.height == expandableLayout.height + onView(withId(R.id.child2)).check(matches(orMoreHeight(1))) + onView(withId(R.id.expandableLayout)).check(matches(equalHeight( + activity.findViewById(R.id.child1), + activity.findViewById(R.id.child2) + ))) + Espresso.unregisterIdlingResources(idlingResource) + + // check toggle (close to first) + instrumentation.runOnMainSync { expandableLayout.toggle() } + idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + // move to first position + onView(withId(R.id.child1)).check(matches(equalHeight(expandableLayout))) + Espresso.unregisterIdlingResources(idlingResource) + + // check toggle open (full) + instrumentation.runOnMainSync { expandableLayout.toggle() } + idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + // move to first position + onView(withId(R.id.expandableLayout)).check(matches(equalHeight( + activity.findViewById(R.id.child1), + activity.findViewById(R.id.child2), + activity.findViewById(R.id.child3) + ))) + Espresso.unregisterIdlingResources(idlingResource) + } +} \ No newline at end of file diff --git a/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivityTest2.kt b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivityTest2.kt new file mode 100644 index 0000000..fea30a1 --- /dev/null +++ b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivityTest2.kt @@ -0,0 +1,96 @@ +package com.github.aakira.expandablelayout.uitest + +import android.app.Activity +import android.support.test.InstrumentationRegistry +import android.support.test.espresso.Espresso +import android.support.test.espresso.Espresso.onView +import android.support.test.espresso.assertion.ViewAssertions.matches +import android.support.test.espresso.matcher.ViewMatchers.withId +import android.support.test.runner.AndroidJUnit4 +import android.test.ActivityInstrumentationTestCase2 +import com.github.aakira.expandablelayout.ExpandableRelativeLayout +import com.github.aakira.expandablelayout.uitest.utils.ElapsedIdLingResource +import com.github.aakira.expandablelayout.uitest.utils.equalHeight +import com.github.aakira.expandablelayout.uitest.utils.equalWidth +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.core.IsNull.notNullValue +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.hamcrest.CoreMatchers.`is` as _is + +@RunWith(AndroidJUnit4::class) +class ExpandableRelativeLayoutActivityTest2 : ActivityInstrumentationTestCase2 +(ExpandableRelativeLayoutActivity2::class.java) { + + companion object { + val DURATION = 350L + } + + @Before + @Throws(Exception::class) + public override fun setUp() { + super.setUp() + injectInstrumentation(InstrumentationRegistry.getInstrumentation()) + } + + @After + @Throws(Exception::class) + public override fun tearDown() { + super.tearDown() + } + + @Test + fun testExpandableRelativeLayout2() { + val activity = activity + val instrumentation = instrumentation + val resources = activity.resources + + // check activity + assertThat(activity, notNullValue()) + assertThat(instrumentation, notNullValue()) + + val expandableLayout = activity.findViewById(R.id.expandableLayout) as ExpandableRelativeLayout + val expandableLayoutPadding = resources.getDimensionPixelSize(R.dimen.margin_large) + + // default close + onView(withId(R.id.expandableLayout)).check(matches(equalHeight(0))) + // width + onView(withId(R.id.child1)).check(matches( + equalWidth(resources.getDimensionPixelSize(R.dimen.relative_layout_2_child_1_width)))) + onView(withId(R.id.child2)).check(matches( + equalWidth(resources.getDimensionPixelSize(R.dimen.relative_layout_2_child_2_width)))) + + // open toggle + instrumentation.runOnMainSync { expandableLayout.toggle() } + var idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + onView(withId(R.id.expandableLayout)).check(matches(equalHeight( + activity.findViewById(R.id.child1), + activity.findViewById(R.id.child3), + margin = expandableLayoutPadding + ))) + Espresso.unregisterIdlingResources(idlingResource) + + // move to first layout + instrumentation.runOnMainSync { expandableLayout.moveChild(0) } + idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + onView(withId(R.id.expandableLayout)).check(matches(equalHeight( + activity.findViewById(R.id.child1), + margin = expandableLayoutPadding + ))) + Espresso.unregisterIdlingResources(idlingResource) + + // move to second layout + instrumentation.runOnMainSync { expandableLayout.moveChild(1) } + idlingResource = ElapsedIdLingResource(DURATION) + Espresso.registerIdlingResources(idlingResource) + onView(withId(R.id.expandableLayout)).check(matches(equalHeight( + activity.findViewById(R.id.child2), + margin = expandableLayoutPadding + ))) + Espresso.unregisterIdlingResources(idlingResource) + } +} \ No newline at end of file diff --git a/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ContextExt.kt b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ContextExt.kt new file mode 100644 index 0000000..c68bdb8 --- /dev/null +++ b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ContextExt.kt @@ -0,0 +1,11 @@ +package com.github.aakira.expandablelayout.uitest.utils + +import android.content.Context +import android.util.DisplayMetrics +import android.view.WindowManager + +fun Context.getDisplayMetrics(): DisplayMetrics { + val metrics = DisplayMetrics() + (getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.getMetrics(metrics) + return metrics +} \ No newline at end of file diff --git a/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ElapsedIdLingResource.java b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ElapsedIdLingResource.java new file mode 100644 index 0000000..9dea783 --- /dev/null +++ b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ElapsedIdLingResource.java @@ -0,0 +1,38 @@ +package com.github.aakira.expandablelayout.uitest.utils; + +import android.support.test.espresso.IdlingResource; + +/** + * Referenced from stack over flow. + * http://stackoverflow.com/questions/30155227/espresso-how-to-wait-for-some-time1-hour + */ +public class ElapsedIdLingResource implements IdlingResource { + private final long startTime; + private final long waitingTime; + private ResourceCallback resourceCallback; + + public ElapsedIdLingResource(long waitingTime) { + this.startTime = System.currentTimeMillis(); + this.waitingTime = waitingTime; + } + + @Override + public String getName() { + return ElapsedIdLingResource.class.getName() + ":" + waitingTime; + } + + @Override + public boolean isIdleNow() { + long elapsed = System.currentTimeMillis() - startTime; + boolean idle = (elapsed >= waitingTime); + if (idle) { + resourceCallback.onTransitionToIdle(); + } + return idle; + } + + @Override + public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { + this.resourceCallback = resourceCallback; + } +} \ No newline at end of file diff --git a/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ViewTestUtil.kt b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ViewTestUtil.kt new file mode 100644 index 0000000..969d98c --- /dev/null +++ b/library-ui-test/src/androidTest/java/com/github/aakira/expandablelayout/uitest/utils/ViewTestUtil.kt @@ -0,0 +1,54 @@ +package com.github.aakira.expandablelayout.uitest.utils + +import android.view.View +import org.hamcrest.Description +import org.hamcrest.TypeSafeMatcher + +fun orMoreHeight(height: Int) = object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText(String.format("The height of this layout " + + "is not or more height (%d).", height)) + } + + override fun matchesSafely(view: View) = view.height > height +} + +fun equalHeight(height: Int) = object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText(String.format("The height of this layout " + + "is not equal to height(%d).", height)) + } + + override fun matchesSafely(view: View) = view.height == height +} + +fun equalHeight(vararg views: View, margin: Int? = null) = object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText(buildString { + append("The height of this layout is not equal to view height(") + views.forEachIndexed { i, view -> + append(view.height) + if (i != views.size - 1) append(" + ") + } + margin?.let { append(" + margin:" + it) } + if (views.size > 0) { + append(" = ") + var sumHeight = views.sumBy { it.height } + margin?.let { sumHeight += it } + append(sumHeight) + } + append(")") + }) + } + + override fun matchesSafely(view: View) = view.height == views.sumBy { it.height } + (margin ?: 0) +} + +fun equalWidth(width: Int) = object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText(String.format("The width of this layout " + + "is not equal to width(%d).", width)) + } + + override fun matchesSafely(view: View) = view.width == width +} \ No newline at end of file diff --git a/library-ui-test/src/main/AndroidManifest.xml b/library-ui-test/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a6c2991 --- /dev/null +++ b/library-ui-test/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/library-ui-test/src/main/kotlin/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivity.kt b/library-ui-test/src/main/kotlin/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivity.kt new file mode 100644 index 0000000..488bc4b --- /dev/null +++ b/library-ui-test/src/main/kotlin/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivity.kt @@ -0,0 +1,12 @@ +package com.github.aakira.expandablelayout.uitest + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity + +class ExpandableRelativeLayoutActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_expandable_relative_layout) + } +} diff --git a/library-ui-test/src/main/kotlin/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivity2.kt b/library-ui-test/src/main/kotlin/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivity2.kt new file mode 100644 index 0000000..ddc202f --- /dev/null +++ b/library-ui-test/src/main/kotlin/com/github/aakira/expandablelayout/uitest/ExpandableRelativeLayoutActivity2.kt @@ -0,0 +1,30 @@ +package com.github.aakira.expandablelayout.uitest + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import com.github.aakira.expandablelayout.ExpandableRelativeLayout +import kotlin.properties.Delegates + +class ExpandableRelativeLayoutActivity2 : AppCompatActivity() { + + companion object { + + @JvmStatic fun startActivity(context: Context) { + context.startActivity(Intent(context, ExpandableRelativeLayoutActivity2::class.java)) + } + } + + private var expandLayout: ExpandableRelativeLayout by Delegates.notNull() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_expandable_relative_layout_2) + supportActionBar?.title = ExpandableRelativeLayoutActivity2::class.java.simpleName + + expandLayout = findViewById(R.id.expandableLayout) as ExpandableRelativeLayout + findViewById(R.id.expandButton).setOnClickListener { expandLayout.toggle() } + findViewById(R.id.moveChildButton).setOnClickListener { expandLayout.moveChild(1) } + } +} \ No newline at end of file diff --git a/library-ui-test/src/main/res/drawable/white.xml b/library-ui-test/src/main/res/drawable/white.xml new file mode 100644 index 0000000..f4ca6d8 --- /dev/null +++ b/library-ui-test/src/main/res/drawable/white.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/library-ui-test/src/main/res/layout/activity_expandable_relative_layout.xml b/library-ui-test/src/main/res/layout/activity_expandable_relative_layout.xml new file mode 100644 index 0000000..9fbcfec --- /dev/null +++ b/library-ui-test/src/main/res/layout/activity_expandable_relative_layout.xml @@ -0,0 +1,117 @@ + + + + + +