-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #85 from KakaoCup/clicks-improvements
Clicks improvements FINAL
- Loading branch information
Showing
52 changed files
with
1,560 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
name: Deploy Clicks Extension to Sonatype | ||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v1 | ||
- name: Set up JDK 11 | ||
uses: actions/setup-java@v1 | ||
with: | ||
java-version: 11 | ||
- uses: little-core-labs/[email protected] | ||
- name: deploy-release-ext-clicks | ||
run: ./gradlew :kakao-ext-clicks:publishDefaultPublicationToOSSHRRepository -PreleaseMode=RELEASE --stacktrace | ||
env: | ||
GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} | ||
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} | ||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
notify: | ||
needs: deploy | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@master | ||
- name: Notify Telegram | ||
uses: appleboy/telegram-action@master | ||
with: | ||
to: ${{ secrets.TELEGRAM_TO }} | ||
token: ${{ secrets.TELEGRAM_TOKEN }} | ||
message: | | ||
New extension version has been released: Clicks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
@file:Suppress("DEPRECATION") | ||
|
||
import com.android.build.gradle.LibraryExtension | ||
import org.gradle.api.Action | ||
import org.gradle.api.Project | ||
import org.gradle.api.plugins.JavaPluginConvention | ||
import org.gradle.api.publish.PublishingExtension | ||
import org.gradle.api.publish.maven.MavenPom | ||
import org.gradle.api.publish.maven.MavenPomContributorSpec | ||
import org.gradle.api.publish.maven.MavenPomDeveloperSpec | ||
import org.gradle.api.publish.maven.MavenPublication | ||
import org.gradle.api.tasks.bundling.Jar | ||
import org.gradle.kotlin.dsl.configure | ||
import org.gradle.kotlin.dsl.creating | ||
import org.gradle.kotlin.dsl.extra | ||
import org.gradle.kotlin.dsl.findByType | ||
import org.gradle.kotlin.dsl.get | ||
import org.gradle.kotlin.dsl.getValue | ||
import org.gradle.kotlin.dsl.provideDelegate | ||
import org.gradle.kotlin.dsl.the | ||
import org.gradle.plugins.signing.SigningExtension | ||
import org.jetbrains.dokka.gradle.DokkaTask | ||
import java.net.URI | ||
|
||
object KakaoExtClicksDeployment { | ||
val ghToken = System.getenv("GH_TOKEN") | ||
val sonatypeUser = System.getenv("SONATYPE_USERNAME") | ||
val sonatypePassword = System.getenv("SONATYPE_PASSWORD") | ||
var releaseMode: String? = null | ||
var versionSuffix: String? = null | ||
var deployUrl: String? = null | ||
|
||
val snapshotDeployUrl = System.getenv("SONATYPE_SNAPSHOTS_URL") | ||
?: "https://s01.oss.sonatype.org/content/repositories/snapshots/" | ||
val releaseDeployUrl = System.getenv("SONATYPE_RELEASES_URL") | ||
?: "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" | ||
|
||
fun initialize(project: Project) { | ||
val releaseMode: String? by project | ||
val versionSuffix = when (releaseMode) { | ||
"RELEASE" -> "" | ||
else -> "-SNAPSHOT" | ||
} | ||
|
||
KakaoExtClicksDeployment.releaseMode = releaseMode | ||
KakaoExtClicksDeployment.versionSuffix = versionSuffix | ||
deployUrl = when (releaseMode) { | ||
"RELEASE" -> releaseDeployUrl | ||
else -> snapshotDeployUrl | ||
} | ||
|
||
initializePublishing(project) | ||
initializeSigning(project) | ||
} | ||
|
||
private fun initializePublishing(project: Project) { | ||
project.version = Versions.kakaoExtClicksVersion + versionSuffix | ||
|
||
project.plugins.apply("maven-publish") | ||
|
||
val (component, additionalArtifacts) = when { | ||
project.extensions.findByType(LibraryExtension::class) != null -> { | ||
val android = project.extensions.findByType(LibraryExtension::class)!! | ||
val main = android.sourceSets.getByName("main") | ||
val sourcesJar by project.tasks.creating(Jar::class) { | ||
classifier = "sources" | ||
from(main.java.srcDirs) | ||
} | ||
|
||
Pair(project.components["release"], listOf(sourcesJar)) | ||
} | ||
project.the(JavaPluginConvention::class) != null -> { | ||
val javaPlugin = project.the(JavaPluginConvention::class) | ||
|
||
val sourcesJar by project.tasks.creating(Jar::class) { | ||
classifier = "sources" | ||
from(javaPlugin.sourceSets["main"].allSource) | ||
} | ||
Pair(project.components["java"], listOf(sourcesJar)) | ||
} | ||
else -> { | ||
throw RuntimeException("Unknown plugin") | ||
} | ||
} | ||
|
||
project.configure<PublishingExtension> { | ||
publications { | ||
create("default", MavenPublication::class.java) { | ||
KakaoExtClicksDeployment.customizePom(pom) | ||
additionalArtifacts.forEach { it -> | ||
artifact(it) | ||
} | ||
from(component) | ||
} | ||
} | ||
repositories { | ||
maven { | ||
name = "Local" | ||
setUrl("${project.rootDir}/build/repository") | ||
} | ||
maven { | ||
name = "OSSHR" | ||
credentials { | ||
username = sonatypeUser | ||
password = sonatypePassword | ||
} | ||
url = URI.create(deployUrl) | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun initializeSigning(project: Project) { | ||
val passphrase = System.getenv("GPG_PASSPHRASE") | ||
passphrase?.let { | ||
project.plugins.apply("signing") | ||
|
||
val publishing = project.the(PublishingExtension::class) | ||
project.configure<SigningExtension> { | ||
sign(publishing.publications.getByName("default")) | ||
} | ||
|
||
project.extra.set("signing.keyId", "0110979F") | ||
project.extra.set("signing.password", passphrase) | ||
project.extra.set("signing.secretKeyRingFile", "${project.rootProject.rootDir}/buildsystem/secring.gpg") | ||
} | ||
} | ||
|
||
fun customizePom(pom: MavenPom?) { | ||
pom?.apply { | ||
name.set("kakao-ext-clicks") | ||
url.set("https://github.com/KakaoCup/Kakao/kakao-ext-clicks") | ||
description.set("Clicks extension for Kakao") | ||
|
||
licenses { | ||
license { | ||
name.set("The Apache License, Version 2.0") | ||
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") | ||
} | ||
} | ||
|
||
scm { | ||
url.set("https://github.com/KakaoCup/Kakao.git") | ||
connection.set("scm:git:ssh://github.com/KakaoCup/Kakao") | ||
developerConnection.set("scm:git:ssh://github.com/KakaoCup/Kakao") | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1.0.0 |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# Kakao custom clicks | ||
|
||
Espresso clicks implementation sometimes causes additional problems while devices are unstable. | ||
It could be a click that was registered as long click. Click that is not registered at all. And so on. | ||
|
||
From the [androidx.test.espresso.action.GeneralClickAction.perform] comment: | ||
|
||
``` | ||
Native event injection is quite a tricky process. A tap is actually 2 | ||
seperate motion events which need to get injected into the system. Injection | ||
makes an RPC call from our app under test to the Android system server, the | ||
system server decides which window layer to deliver the event to, the system | ||
server makes an RPC to that window layer, that window layer delivers the event | ||
to the correct UI element, activity, or window object. Now we need to repeat | ||
that 2x. for a simple down and up. Oh and the down event triggers timers to | ||
detect whether or not the event is a long vs. short press. The timers are | ||
removed the moment the up event is received (NOTE: the possibility of eventTime | ||
being in the future is totally ignored by most motion event processors). | ||
Phew. | ||
The net result of this is sometimes we'll want to do a regular tap, and for | ||
whatever reason the up event (last half) of the tap is delivered after long | ||
press timeout (depending on system load) and the long press behaviour is | ||
displayed (EG: show a context menu). There is no way to avoid or handle this more | ||
gracefully. Also the longpress behavour is app/widget specific. So if you have | ||
a seperate long press behaviour from your short press, you can pass in a | ||
'RollBack' ViewAction which when executed will undo the effects of long press. | ||
``` | ||
|
||
If you experience unreliable tap/click in UI tests you can try our naive but sometimes more reliable implementation, that dispatches events | ||
directly to View. | ||
|
||
## How to use | ||
|
||
There are multiple ways to apply custom clicks: | ||
|
||
### Apply KakaoClicksTestRule | ||
|
||
If you want to apply it directly to single test class the TestRule is an obvious choice. | ||
|
||
For example: | ||
``` | ||
@Rule | ||
@JvmField | ||
var chain: TestRule = RuleChain.outerRule(ActivityScenarioRule(MyActivity::class.java)) | ||
.around(KakaoClicksTestRule()) | ||
``` | ||
|
||
### Override Kakao clicks behaviour | ||
|
||
If you need to change it globally, you can override static variables of kakao, like that: | ||
|
||
``` | ||
Kakao { | ||
singleClickAction = KakaoSingleClick() | ||
doubleClickAction = KakaoDoubleClick() | ||
longClickAction = KakaoLongClick() | ||
} | ||
``` | ||
|
||
To revert this behavior you can set it back: | ||
|
||
``` | ||
Kakao { | ||
singleClickAction = EspressoSingleClick() | ||
doubleClickAction = EspressoDoubleClick() | ||
longClickAction = EspressoLongClick() | ||
} | ||
``` | ||
|
||
To make a precise change for single click you can implement your own function and execute your test code wrapped in it: | ||
|
||
```kotlin | ||
fun withCustomClicks(block: () -> Unit) { | ||
Kakao { | ||
singleClickAction = KakaoSingleClick() | ||
doubleClickAction = KakaoDoubleClick() | ||
longClickAction = KakaoLongClick() | ||
} | ||
|
||
block.invoke() | ||
|
||
Kakao { | ||
singleClickAction = EspressoSingleClick() | ||
doubleClickAction = EspressoDoubleClick() | ||
longClickAction = EspressoLongClick() | ||
} | ||
} | ||
|
||
button { | ||
withCustomClicks { | ||
click() // clicked with custom mechanism | ||
} | ||
click() // clicked by espresso | ||
} | ||
``` | ||
|
||
## Click visualization | ||
|
||
Click visualization is a useful debug tool. Usually it's enabled with Android option `Developer Options > Show taps`. Or using ADB: | ||
|
||
``` | ||
adb shell settings put system show_touches 1 | ||
``` | ||
|
||
On different setups it can be impossible to set or it can simply not working. | ||
|
||
To enable visual taps programmatically with custom clicks, use custom click constructor: | ||
|
||
```kotlin | ||
KakaoSingleClick(visualClicksConfig = VisualClicksConfig()) // null is the default argument | ||
KakaoDoubleClick(visualClicksConfig = VisualClicksConfig()) | ||
KakaoLongClick(visualClicksConfig = VisualClicksConfig()) | ||
``` | ||
|
||
or if you are using TestRule: | ||
|
||
```kotlin | ||
KakaoClicksTestRule(visualClicksConfig = VisualClicksConfig()) | ||
``` | ||
|
||
to apply config to all types of clicks | ||
|
||
`VisualClicksConfig` data class has some customization options: like color and radius of the tap circle. | ||
|
||
## Global Center coordinates | ||
|
||
There are some cases when standard espresso coordinates not working. | ||
For example clicking on center of the view with applied property animations or transitions with help of `GeneralLocation.VISIBLE_CENTER`. | ||
See explanation on why it happens [here](https://github.com/avito-tech/avito-android/pull/308). | ||
|
||
`VisibleCenterGlobalCoordinatesProvider` to the rescue. | ||
|
||
Pass it as a replacement for your click location like that: | ||
|
||
```kotlin | ||
tranformedView.click(VisibleCenterGlobalCoordinatesProvider()) | ||
``` | ||
|
||
## Customize emulator | ||
|
||
You can also tune emulator a bit that could help with long click registration. | ||
|
||
``` | ||
adb shell "settings put secure long_press_timeout 1500" // default can vary but usually it's 400ms | ||
``` | ||
|
||
## Credits | ||
|
||
Initial idea and implementation by [Avito](https://github.com/avito-tech/avito-android) |
Oops, something went wrong.