├── .github
└── CODEOWNERS
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── THANKS.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── mapbox
│ │ └── android
│ │ └── gestures
│ │ ├── GesturesUiTestUtils.kt
│ │ ├── MoveGestureDetectorTest.kt
│ │ ├── MultiFingerTapGestureDetectorTest.kt
│ │ └── ScaleGestureDetectorTest.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── mapbox
│ │ └── android
│ │ └── gestures
│ │ └── testapp
│ │ ├── HelpDialogFragment.java
│ │ ├── MainActivity.java
│ │ ├── MapboxActivity.java
│ │ ├── MyApplication.java
│ │ ├── TestActivity.java
│ │ ├── TestView.java
│ │ └── Utils.java
│ └── res
│ ├── drawable-hdpi
│ ├── ic_help_outline.png
│ └── logo.png
│ ├── drawable-mdpi
│ ├── ic_help_outline.png
│ └── logo.png
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable-xhdpi
│ ├── ic_help_outline.png
│ └── logo.png
│ ├── drawable-xxhdpi
│ ├── ic_help_outline.png
│ └── logo.png
│ ├── drawable-xxxhdpi
│ ├── ic_help_outline.png
│ └── logo.png
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── activity_mapbox.xml
│ ├── activity_test.xml
│ └── fragment_help_dialog.xml
│ ├── menu
│ └── menu.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── circle.yml
├── config
└── checkstyle
│ └── checkstyle.xml
├── gradle.properties
├── gradle
├── artifact-settings.gradle
├── checkstyle.gradle
├── dependencies.gradle
├── javadoc.gradle
├── sdk-registry.gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── mapbox
│ │ │ └── android
│ │ │ └── gestures
│ │ │ ├── AndroidGesturesManager.java
│ │ │ ├── BaseGesture.java
│ │ │ ├── Constants.java
│ │ │ ├── MoveDistancesObject.java
│ │ │ ├── MoveGestureDetector.java
│ │ │ ├── MultiFingerDistancesObject.java
│ │ │ ├── MultiFingerGesture.java
│ │ │ ├── MultiFingerTapGestureDetector.java
│ │ │ ├── PermittedActionsGuard.java
│ │ │ ├── PointerDistancePair.java
│ │ │ ├── ProgressiveGesture.java
│ │ │ ├── RotateGestureDetector.java
│ │ │ ├── ShoveGestureDetector.java
│ │ │ ├── SidewaysShoveGestureDetector.java
│ │ │ ├── StandardGestureDetector.java
│ │ │ ├── StandardScaleGestureDetector.java
│ │ │ └── Utils.java
│ └── res
│ │ └── values
│ │ └── dimens.xml
│ └── test
│ └── java
│ └── com
│ └── mapbox
│ └── android
│ └── gestures
│ ├── AbstractGestureDetectorTest.java
│ ├── AndroidGesturesManagerTest.java
│ ├── MoveGestureDetectorTest.java
│ ├── MultiFingerTapGestureDetectorTest.java
│ ├── PointersManagementTest.java
│ ├── RotateGestureDetectorTest.java
│ ├── ShoveGestureDetectorTest.java
│ ├── SidewaysShoveGestureDetectorTest.java
│ ├── StandardGestureDetectorTest.java
│ └── TestUtils.kt
└── settings.gradle
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @mapbox/maps-android
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle/
3 | local.properties
4 | .idea/
5 | *.iml
6 | .DS_Store
7 | build/
8 | captures/
9 | .externalNativeBuild
10 | app/src/main/res/values/developer-config.xml
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog for the Mapbox Gestures for Android
2 |
3 | ## 0.9.1 - November 27, 2023
4 | Minor release with internal fixes for publishing library
5 |
6 | ## 0.9.0 - November 24, 2023
7 | #### Breaking changes
8 | - Increase minimum supported Android SDK version to 19 (KITKAT)
9 | #### Minor features
10 | - Support compile SDK 30
11 |
12 | ## 0.8.0 - August 19, 2022
13 | #### Minor features
14 | - Updated dependencies : Maps 10.7.0, targetSdk 31, compileSdk 30. [#101](https://github.com/mapbox/mapbox-gestures-android/pull/101)
15 | #### Breaking changes
16 | - Remove NonNull annotations from SimpleStandardOnGestureListener that have no such annotations in Android sources. [#101](https://github.com/mapbox/mapbox-gestures-android/pull/101)
17 |
18 | ## 0.7.0 - April 30, 2020
19 | #### Minor features
20 | - Expose a `MoveGestureDetector#moveThresholdRect`. When set, the defined screen area prohibits move gesture to be started. [#96](https://github.com/mapbox/mapbox-gestures-android/pull/96)
21 |
22 | ## 0.6.0 - January 8, 2020
23 | #### Major changes
24 | - Replace appcompat dependencies with AndroidX [#94](https://github.com/mapbox/mapbox-gestures-android/pull/94)
25 |
26 | #### Minor features and bug fixes
27 | - Use NonNull in gesture listener interfaces to improve consumption with the Kotlin programming language [#95](https://github.com/mapbox/mapbox-gestures-android/pull/95)
28 |
29 | ## 0.5.1 - August 20, 2019
30 | #### Bug fixes
31 | - Fixed a bug where quick-scale was registered during a move gesture that followed a double-tap [#88](https://github.com/mapbox/mapbox-gestures-android/pull/88)
32 |
33 | ## 0.5.0 - August 14, 2019
34 | #### Major changes
35 | - Introduce a custom scale gesture detector implementation. The library doesn't rely on the compat gesture detector anymore. This is a breaking changing because the underlying scale gesture detector reference has been removed. [#73](https://github.com/mapbox/mapbox-gestures-android/pull/73)
36 |
37 | #### Minor features and bug fixes
38 | - Calculate focal point for every motion event, not only MOVE. Fixes an issue where detectors that do not rely on movement would return cached, historic focal points. [#77](https://github.com/mapbox/mapbox-gestures-android/pull/77)
39 | - Adjust scale gesture's required pointer count based on type. Fixes an issue where quick-scale was not properly interrupted. [#74](https://github.com/mapbox/mapbox-gestures-android/pull/74)
40 | - Guard against move events coming from different view trees. Might prevent rare crashes that are out of control of the gestures library. [#71](https://github.com/mapbox/mapbox-gestures-android/pull/71)
41 | - Expose scale span getters. [#75](https://github.com/mapbox/mapbox-gestures-android/pull/75)
42 |
43 | ## 0.4.2 - April 26, 2019
44 | - Query display metrics only in touch down [#67](https://github.com/mapbox/mapbox-gestures-android/pull/67)
45 |
46 | ## 0.4.1 - April 16, 2019
47 | - Try getting real device display metrics for sloppy gesture calculations [#61](https://github.com/mapbox/mapbox-gestures-android/pull/61)
48 | - Remove obsolete string values [#62](https://github.com/mapbox/mapbox-gestures-android/pull/62)
49 |
50 | ## 0.4.0 - January 31, 2019
51 | - Removed Timber dependency [#54](https://github.com/mapbox/mapbox-gestures-android/pull/54)
52 | - Prepare the project to be consumed as a submodule [#55](https://github.com/mapbox/mapbox-gestures-android/pull/55)
53 | - Update tooling and CI image [#56](https://github.com/mapbox/mapbox-gestures-android/pull/56)
54 | - Remove deprecated javadoc source declaration [#58](https://github.com/mapbox/mapbox-gestures-android/pull/58)
55 | - Exclude maven plugin and checkstyle from the child build.gradle [#57](https://github.com/mapbox/mapbox-gestures-android/pull/57)
56 |
57 | ## 0.3.0 - October 30, 2018
58 | - Increase missing events protection [#46](https://github.com/mapbox/mapbox-gestures-android/pull/46)
59 | - Limit support library usage [#47](https://github.com/mapbox/mapbox-gestures-android/pull/47)
60 |
61 | ## 0.2.0 - March 27, 2018
62 | - SidewaysShoveGestureDetector [#27](https://github.com/mapbox/mapbox-gestures-android/pull/27)
63 | - Decrease minimum span required to register scale gesture [#30](https://github.com/mapbox/mapbox-gestures-android/pull/30)
64 |
65 | ## 0.1.0 - March 19, 2018
66 | - Initial release!
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We welcome contributions to this gestures repository. If you're interested in helping develop the library, please follow these steps:
4 |
5 | - [Open a ticket](https://github.com/mapbox/mapbox-gestures-android/issues/new) to kick off a conversation, feel free to tag the `@mapbox/android` team. It's a good idea to explain your plans before you push any code to make sure no one else is working on something similar and to discuss the best approaches to tackle your particular idea.
6 |
7 | - Create a new branch that will contain the code for your additions.
8 |
9 | - Pull requests are gladly accepted. If there are any changes that developers should be aware of, please update the [change log](CHANGELOG.md)
10 |
11 | - Mapbox uses checkstyle to enforce good Java code standards. Make sure to read the [Mapbox GL Native Wiki entry](https://github.com/mapbox/mapbox-gl-native/wiki/Setting-up-Mapbox-checkstyle) and setup. CI will fail if your PR contains any mistakes.
12 |
13 | # Code of conduct
14 | Everyone is invited to participate in Mapbox’s open source projects and public discussions: we want to create a welcoming and friendly environment. Harassment of participants or other unethical and unprofessional behavior will not be tolerated in our spaces. The [Contributor Covenant](http://contributor-covenant.org) applies to all projects under the Mapbox organization and we ask that you please read [the full text](http://contributor-covenant.org/version/1/2/0/).
15 |
16 | You can learn more about our open source philosophy on [mapbox.com](https://www.mapbox.com/about/open/).
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2018, Mapbox
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
26 | ===========================================================================
27 |
28 | Mapbox Gestures for Android uses portions of Android Gesture Detectors Framework.
29 |
30 | Copyright (c) 2012, Almer Thie
31 |
32 | All rights reserved.
33 |
34 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
35 |
36 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
37 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
38 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 |
40 | ===========================================================================
41 |
42 | Mapbox Gestures for Android uses portions of Android Support Library.
43 |
44 | Copyright (c) 2005-2013, The Android Open Source Project
45 |
46 | Licensed under the Apache License, Version 2.0 (the "License");
47 | you may not use this file except in compliance with the License.
48 | You may obtain a copy of the License at
49 |
50 | http://www.apache.org/licenses/LICENSE-2.0
51 |
52 | Unless required by applicable law or agreed to in writing, software
53 | distributed under the License is distributed on an "AS IS" BASIS,
54 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
55 | See the License for the specific language governing permissions and
56 | limitations under the License.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | checkstyle:
2 | ./gradlew checkstyle
3 |
4 | javadoc:
5 | # Output is (module)/build/docs/javadoc/release
6 | ./gradlew library:javadocrelease
7 |
8 | test:
9 | ./gradlew :library:test -i
10 |
11 | release:
12 | ./gradlew :library:assembleRelease
13 |
14 | sdkRegistryUpload:
15 | ./gradlew :library:mapboxSDKRegistryUpload
16 |
17 | sdkRegistryPublish:
18 | ./gradlew :library:mapboxSDKRegistryPublish
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mapbox Gestures for Android
2 |
3 | The Mapbox Gestures for Android library wraps [GestureDetectorCompat](https://developer.android.com/reference/android/support/v4/view/GestureDetectorCompat.html) and introduces implementation of scale, rotate, move, shove and tap gesture detectors.
4 |
5 | Mapbox Gestures for Android was inspired by [Android Gesture Detector Framework](https://github.com/Almeros/android-gesture-detectors) and offers the same functionality with some additional features on top.
6 |
7 | The library is implemented in the projects found below, where you can head for more examples:
8 |
9 | - [The Mapbox Maps SDK for Android](https://github.com/mapbox/mapbox-maps-android)
10 | - [This library's sample app](https://github.com/mapbox/mapbox-gestures-android/tree/master/app/src/main/java/com/mapbox/android/gestures/testapp) included in this repository
11 |
12 | Are you using the library in your project as well? Let us know or create a PR, we'll be more than happy to add it to the list!
13 |
14 |
15 | ## Documentation
16 |
17 | You'll find all of this library's documentation on [our Maps Guides user interaction page](https://docs.mapbox.com/android/maps/guides/user-interaction/). This includes information on installation, using the API, and links to the API reference.
18 |
19 |
20 | ## Getting Started
21 |
22 | If you are looking to include Mapbox Gestures for Android inside of your project, please take a look at [the detailed instructions](https://docs.mapbox.com/android/maps/guides/user-interaction/) found in our docs. If you are interested in building from source, read the contributing guide inside of this project.
23 |
24 | To use the Gestures library, you need to set the SDK registry (own Mapbox Maven repo) first.
25 | Please follow the instructions provided at the general [Maps install page](https://docs.mapbox.com/android/maps/guides/install/#add-the-dependency).
26 |
27 | Then add the Mapbox Android Gestures library :
28 |
29 | ```java
30 | // In the app build.gradle file
31 | dependencies {
32 | implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.8.0'
33 | }
34 | ```
35 |
36 | #### Mapbox access tokens
37 |
38 | To build test application you need to configure Mapbox access tokens as described at https://docs.mapbox.com/android/maps/guides/install/#configure-credentials.
39 | To build the project you need to specify SDK_REGISTRY_TOKEN as an environmental variable or a gradle property. It is a secret token used to access the SDK Registry (Mapbox Maven instance) during compile time, with a scope set to `DOWNLOADS:READ`.
40 | To run the specific Mapbox activity in this repo's test application, you need to include public token in the [app/src/main/res/values/developer-config.xml] resource file.
41 |
42 | ## Getting Help
43 |
44 | - **Need help with your code?**: Look for previous questions on the [#mapbox tag](https://stackoverflow.com/questions/tagged/mapbox+android) — or [ask a new question](https://stackoverflow.com/questions/tagged/mapbox+android).
45 | - **Have a bug to report?** [Open an issue](https://github.com/mapbox/mapbox-gestures-android/issues). If possible, include the version of Mapbox Core that you're using, a full log, and a project that shows the issue.
46 | - **Have a feature request?** [Open an issue](https://github.com/mapbox/mapbox-gestures-android/issues/new). Tell us what the feature should do and why you want the feature.
47 |
48 | ## Sample code
49 |
50 | [This repo's test app](https://github.com/mapbox/mapbox-gestures-android/tree/master/app/src/main/java/com/mapbox/android/gestures/testapp) can also help you get started with the Gestures library.
51 |
52 | ## Contributing
53 |
54 | We welcome feedback, translations, and code contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
55 |
56 | ## Version
57 |
58 | Noting here, that `0.x` versions series of `Mapbox Gestures for Android` is still in an experimental phase. Breaking changes can occur with every iteration.
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/THANKS.md:
--------------------------------------------------------------------------------
1 | Authors of this library would like to send huge thanks to creators of [Android Gesture Detectors Framework](https://github.com/Almeros/android-gesture-detectors) for inspiration and a great library that served us for so many years.
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion androidVersions.compileSdkVersion
7 |
8 | defaultConfig {
9 | applicationId "com.mapbox.android.gestures.testapp"
10 | minSdkVersion androidVersions.minSdkVersion
11 | targetSdkVersion androidVersions.targetSdkVersion
12 | versionCode 1
13 | versionName "0.1.0"
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | compileOptions {
18 | sourceCompatibility JavaVersion.VERSION_1_8
19 | targetCompatibility JavaVersion.VERSION_1_8
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | testOptions {
30 | unitTests {
31 | returnDefaultValues true
32 | // Roboelectric 4.0 required config
33 | // http://robolectric.org/migrating/#migrating-to-40
34 |
35 | includeAndroidResources = true
36 | }
37 | }
38 | }
39 |
40 | dependencies {
41 | implementation dependenciesList.kotlinLib
42 | implementation dependenciesList.supportAppcompatV7
43 | implementation dependenciesList.timber
44 | implementation(dependenciesList.mapboxMaps) {
45 | exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-gestures'
46 | }
47 | implementation("androidx.core:core-ktx") {
48 | version {
49 | strictly("1.6.0")
50 | }
51 | }
52 | implementation("androidx.core:core") {
53 | version {
54 | strictly("1.6.0")
55 | }
56 | }
57 |
58 |
59 | testImplementation dependenciesList.junit
60 | testImplementation dependenciesList.mockito
61 | testImplementation dependenciesList.robolectric
62 | androidTestImplementation dependenciesList.mockitoAndroid
63 | androidTestImplementation dependenciesList.testRunner
64 | androidTestImplementation dependenciesList.testEspressoCore
65 | androidTestImplementation dependenciesList.testEspressoIntents
66 |
67 | implementation project(":library")
68 | }
69 |
70 | apply from: "${rootDir}/gradle/checkstyle.gradle"
71 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/mapbox/android/gestures/MoveGestureDetectorTest.kt:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures
2 |
3 | import GesturesUiTestUtils.DEFAULT_GESTURE_DURATION
4 | import GesturesUiTestUtils.move
5 | import android.graphics.PointF
6 | import android.graphics.RectF
7 | import androidx.test.espresso.Espresso
8 | import androidx.test.espresso.matcher.ViewMatchers
9 | import androidx.test.ext.junit.runners.AndroidJUnit4
10 | import androidx.test.rule.ActivityTestRule
11 | import com.mapbox.android.gestures.testapp.R
12 | import com.mapbox.android.gestures.testapp.TestActivity
13 | import org.junit.Assert
14 | import org.junit.Before
15 | import org.junit.Rule
16 | import org.junit.Test
17 | import org.junit.runner.RunWith
18 | import java.util.concurrent.CountDownLatch
19 | import java.util.concurrent.TimeUnit
20 |
21 | @RunWith(AndroidJUnit4::class)
22 | class MoveGestureDetectorTest {
23 |
24 | @Rule
25 | @JvmField
26 | val activityTestRule = ActivityTestRule(TestActivity::class.java)
27 |
28 | private lateinit var gesturesManager: AndroidGesturesManager
29 |
30 | @Before
31 | fun setup() {
32 | gesturesManager = activityTestRule.activity.gesturesManager
33 | }
34 |
35 | @Test
36 | fun move_ignoredWithRectThreshold() {
37 | val rect = RectF(400f, 400f, 600f, 600f)
38 | gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
39 | override fun onMoveBegin(detector: MoveGestureDetector) = true
40 |
41 | override fun onMove(
42 | detector: MoveGestureDetector,
43 | distanceX: Float,
44 | distanceY: Float
45 | ): Boolean = throw AssertionError("onMove shouldn't be called if threshold was not met")
46 |
47 | override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
48 |
49 | })
50 | gesturesManager.moveGestureDetector.moveThresholdRect = rect
51 | Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
52 | move(
53 | deltaX = 50f,
54 | deltaY = 50f,
55 | startPoint = PointF(rect.right - 100f, rect.bottom - 100f)
56 | )
57 | )
58 | }
59 |
60 | @Test
61 | fun move_executedWhenOutsideOfRect() {
62 | val latch = CountDownLatch(1)
63 | val rect = RectF(400f, 400f, 600f, 600f)
64 | gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
65 | override fun onMoveBegin(detector: MoveGestureDetector) = true
66 |
67 | override fun onMove(
68 | detector: MoveGestureDetector,
69 | distanceX: Float,
70 | distanceY: Float
71 | ): Boolean {
72 | Assert.assertFalse(rect.contains(detector.focalPoint.x, detector.focalPoint.y))
73 | latch.countDown()
74 | return true
75 | }
76 |
77 | override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
78 |
79 | })
80 | gesturesManager.moveGestureDetector.moveThresholdRect = rect
81 | Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
82 | move(
83 | deltaX = 100f,
84 | deltaY = 100f,
85 | startPoint = PointF(rect.right + 50f, rect.bottom + 50f)
86 | )
87 | )
88 | if (!latch.await(DEFAULT_GESTURE_DURATION, TimeUnit.MILLISECONDS)) {
89 | Assert.fail("move was not called")
90 | }
91 | }
92 |
93 | @Test
94 | fun move_executedWhenRectThresholdMet() {
95 | val latch = CountDownLatch(1)
96 | val rect = RectF(400f, 400f, 600f, 600f)
97 | gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
98 | override fun onMoveBegin(detector: MoveGestureDetector) = true
99 |
100 | override fun onMove(
101 | detector: MoveGestureDetector,
102 | distanceX: Float,
103 | distanceY: Float
104 | ): Boolean {
105 | Assert.assertFalse(rect.contains(detector.focalPoint.x, detector.focalPoint.y))
106 | latch.countDown()
107 | return true
108 | }
109 |
110 | override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
111 |
112 | })
113 | gesturesManager.moveGestureDetector.moveThresholdRect = rect
114 | Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
115 | move(
116 | deltaX = -150f,
117 | deltaY = -150f,
118 | startPoint = PointF(500f, 500f)
119 | )
120 | )
121 | if (!latch.await(DEFAULT_GESTURE_DURATION, TimeUnit.MILLISECONDS)) {
122 | Assert.fail("move was not called")
123 | }
124 | }
125 |
126 | @Test
127 | fun move_whenOutsideOfRect_executeWhenMoveThresholdMet() {
128 | val latch = CountDownLatch(1)
129 | val rect = RectF(400f, 400f, 600f, 600f)
130 | gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
131 | override fun onMoveBegin(detector: MoveGestureDetector) = true
132 |
133 | override fun onMove(
134 | detector: MoveGestureDetector,
135 | distanceX: Float,
136 | distanceY: Float
137 | ): Boolean {
138 | Assert.assertFalse(rect.contains(detector.focalPoint.x, detector.focalPoint.y))
139 | latch.countDown()
140 | return true
141 | }
142 |
143 | override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
144 |
145 | })
146 | gesturesManager.moveGestureDetector.moveThresholdRect = rect
147 | gesturesManager.moveGestureDetector.moveThreshold = 50f
148 | Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
149 | move(
150 | deltaX = 100f,
151 | deltaY = 100f,
152 | startPoint = PointF(rect.right + 50f, rect.bottom + 50f)
153 | )
154 | )
155 | if (!latch.await(DEFAULT_GESTURE_DURATION, TimeUnit.MILLISECONDS)) {
156 | Assert.fail("move was not called")
157 | }
158 | }
159 |
160 | @Test
161 | fun move_whenOutsideOfRect_ignoredWhenMoveThreshold() {
162 | val rect = RectF(400f, 400f, 600f, 600f)
163 | gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
164 | override fun onMoveBegin(detector: MoveGestureDetector) = true
165 |
166 | override fun onMove(
167 | detector: MoveGestureDetector,
168 | distanceX: Float,
169 | distanceY: Float
170 | ): Boolean = throw AssertionError("onMove shouldn't be called if threshold was not met")
171 |
172 | override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
173 |
174 | })
175 | gesturesManager.moveGestureDetector.moveThresholdRect = rect
176 | gesturesManager.moveGestureDetector.moveThreshold = 50f
177 | Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
178 | move(
179 | deltaX = 25f,
180 | deltaY = 25f,
181 | startPoint = PointF(rect.right + 50f, rect.bottom + 50f)
182 | )
183 | )
184 | }
185 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/mapbox/android/gestures/MultiFingerTapGestureDetectorTest.kt:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures
2 |
3 | import GesturesUiTestUtils.DEFAULT_GESTURE_DURATION
4 | import GesturesUiTestUtils.twoTap
5 | import androidx.test.espresso.Espresso
6 | import androidx.test.espresso.matcher.ViewMatchers
7 | import androidx.test.rule.ActivityTestRule
8 | import androidx.test.ext.junit.runners.AndroidJUnit4
9 | import com.mapbox.android.gestures.testapp.R
10 | import com.mapbox.android.gestures.testapp.TestActivity
11 | import org.junit.Assert
12 | import org.junit.Before
13 | import org.junit.Rule
14 | import org.junit.Test
15 | import org.junit.runner.RunWith
16 | import java.util.concurrent.CountDownLatch
17 | import java.util.concurrent.TimeUnit
18 |
19 | @RunWith(AndroidJUnit4::class)
20 | class MultiFingerTapGestureDetectorTest {
21 |
22 | @Rule
23 | @JvmField
24 | val activityTestRule = ActivityTestRule(TestActivity::class.java)
25 |
26 | private lateinit var gesturesManager: AndroidGesturesManager
27 |
28 | @Before
29 | fun setup() {
30 | gesturesManager = activityTestRule.activity.gesturesManager
31 | }
32 |
33 | @Test
34 | fun noMove_focalPoint_invalidated() {
35 | val latch = CountDownLatch(1)
36 | gesturesManager.setMultiFingerTapGestureListener { detector, pointersCount ->
37 | Assert.assertEquals(2, pointersCount)
38 | Assert.assertEquals(Utils.determineFocalPoint(detector.currentEvent), detector.focalPoint)
39 | latch.countDown()
40 | true
41 | }
42 | Espresso.onView(ViewMatchers.withId(R.id.content)).perform(twoTap(300f))
43 |
44 | if (!latch.await(DEFAULT_GESTURE_DURATION, TimeUnit.MILLISECONDS)) {
45 | Assert.fail("two-tap was not called")
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 | * Returns the Mapbox access token set in the app resources. 12 | *
13 | * It will attempt to load the access token from the 14 | * {@code res/values/developer-config.xml} development file. 15 | * 16 | * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}. 17 | * @return The Mapbox access token or null if not found. 18 | */ 19 | public static String getMapboxAccessToken(@NonNull Context context) { 20 | int tokenResId = context.getResources().getIdentifier( 21 | "mapbox_access_token", "string", context.getPackageName()); 22 | return tokenResId != 0 ? context.getString(tokenResId) : null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_help_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapbox-gestures-android/60bcff932b6fc00d1fa4dab660af8787aaa48369/app/src/main/res/drawable-hdpi/ic_help_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapbox-gestures-android/60bcff932b6fc00d1fa4dab660af8787aaa48369/app/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_help_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapbox-gestures-android/60bcff932b6fc00d1fa4dab660af8787aaa48369/app/src/main/res/drawable-mdpi/ic_help_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/mapbox-gestures-android/60bcff932b6fc00d1fa4dab660af8787aaa48369/app/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 |102 | * This is a duration of the user's total interaction with the touch screen, 103 | * accounting for the time before the gesture was recognized by the detector. 104 | * 105 | * @return duration of the gesture in millis. 106 | */ 107 | public long getGestureDuration() { 108 | return gestureDuration; 109 | } 110 | 111 | /** 112 | * Returns most recent event in this gesture chain. 113 | * 114 | * @return most recent event 115 | */ 116 | public MotionEvent getCurrentEvent() { 117 | return currentEvent; 118 | } 119 | 120 | /** 121 | * Returns previous event in this gesture chain. 122 | * 123 | * @return previous event 124 | */ 125 | public MotionEvent getPreviousEvent() { 126 | return previousEvent; 127 | } 128 | 129 | /** 130 | * Check whether this detector accepts and analyzes motion events. Default is true. 131 | * 132 | * @return true if it analyzes, false otherwise 133 | */ 134 | public boolean isEnabled() { 135 | return isEnabled; 136 | } 137 | 138 | /** 139 | * Set whether this detector should accept and analyze motion events. Default is true. 140 | * 141 | * @param enabled true if it should analyze, false otherwise 142 | */ 143 | public void setEnabled(boolean enabled) { 144 | isEnabled = enabled; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /library/src/main/java/com/mapbox/android/gestures/Constants.java: -------------------------------------------------------------------------------- 1 | package com.mapbox.android.gestures; 2 | 3 | public final class Constants { 4 | 5 | /** 6 | * Default angle change required in rotation movement to register rotate gesture./ 7 | */ 8 | public static final float DEFAULT_ROTATE_ANGLE_THRESHOLD = 15.3f; 9 | 10 | /** 11 | * Default angle between pointers (starting from horizontal line) required to abort shove gesture. 12 | */ 13 | public static final float DEFAULT_SHOVE_MAX_ANGLE = 20f; 14 | 15 | /** 16 | * Default time within which pointers need to leave the screen to register tap gesture. 17 | */ 18 | public static final long DEFAULT_MULTI_TAP_TIME_THRESHOLD = 150L; 19 | 20 | 21 | /*Private constants*/ 22 | static final String internal_scaleGestureDetectorMinSpanField = "mMinSpan"; 23 | static final String internal_scaleGestureDetectorSpanSlopField = "mSpanSlop"; 24 | } 25 | -------------------------------------------------------------------------------- /library/src/main/java/com/mapbox/android/gestures/MoveDistancesObject.java: -------------------------------------------------------------------------------- 1 | package com.mapbox.android.gestures; 2 | 3 | /** 4 | * Class that holds initial, previous and current X and Y on-screen coordinates for active pointers. 5 | */ 6 | public final class MoveDistancesObject { 7 | private final float initialX; 8 | private final float initialY; 9 | 10 | private float prevX; 11 | private float prevY; 12 | private float currX; 13 | private float currY; 14 | 15 | private float distanceXSinceLast; 16 | private float distanceYSinceLast; 17 | private float distanceXSinceStart; 18 | private float distanceYSinceStart; 19 | 20 | public MoveDistancesObject(float initialX, float initialY) { 21 | this.initialX = initialX; 22 | this.initialY = initialY; 23 | } 24 | 25 | /** 26 | * Add a new position of this pointer and recalculate distances. 27 | * @param x new X coordinate 28 | * @param y new Y coordinate 29 | */ 30 | public void addNewPosition(float x, float y) { 31 | prevX = currX; 32 | prevY = currY; 33 | 34 | currX = x; 35 | currY = y; 36 | 37 | distanceXSinceLast = prevX - currX; 38 | distanceYSinceLast = prevY - currY; 39 | 40 | distanceXSinceStart = initialX - currX; 41 | distanceYSinceStart = initialY - currY; 42 | } 43 | 44 | /** 45 | * Get X coordinate of this pointer when it was first register. 46 | * @return X coordinate 47 | */ 48 | public float getInitialX() { 49 | return initialX; 50 | } 51 | 52 | /** 53 | * Get Y coordinate of this pointer when it was first register. 54 | * @return Y coordinate 55 | */ 56 | public float getInitialY() { 57 | return initialY; 58 | } 59 | 60 | /** 61 | * Get previous X coordinate of this pointer. 62 | * @return X coordinate 63 | */ 64 | public float getPreviousX() { 65 | return prevX; 66 | } 67 | 68 | /** 69 | * Get previous Y coordinate of this pointer. 70 | * @return Y coordinate 71 | */ 72 | public float getPreviousY() { 73 | return prevY; 74 | } 75 | 76 | /** 77 | * Get current X coordinate of this pointer. 78 | * @return X coordinate 79 | */ 80 | public float getCurrentX() { 81 | return currX; 82 | } 83 | 84 | /** 85 | * Get current Y coordinate of this pointer. 86 | * @return Y coordinate 87 | */ 88 | public float getCurrentY() { 89 | return currY; 90 | } 91 | 92 | /** 93 | * Get X distance covered by this pointer since previous position. 94 | * @return X distance 95 | */ 96 | public float getDistanceXSinceLast() { 97 | return distanceXSinceLast; 98 | } 99 | 100 | /** 101 | * Get Y distance covered by this pointer since previous position. 102 | * @return Y distance 103 | */ 104 | public float getDistanceYSinceLast() { 105 | return distanceYSinceLast; 106 | } 107 | 108 | /** 109 | * Get X distance covered by this pointer since start position. 110 | * @return X distance 111 | */ 112 | public float getDistanceXSinceStart() { 113 | return distanceXSinceStart; 114 | } 115 | 116 | /** 117 | * Get Y distance covered by this pointer since start position. 118 | * @return Y distance 119 | */ 120 | public float getDistanceYSinceStart() { 121 | return distanceYSinceStart; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /library/src/main/java/com/mapbox/android/gestures/MoveGestureDetector.java: -------------------------------------------------------------------------------- 1 | package com.mapbox.android.gestures; 2 | 3 | import android.content.Context; 4 | import android.graphics.PointF; 5 | import android.graphics.RectF; 6 | import android.view.MotionEvent; 7 | 8 | import androidx.annotation.DimenRes; 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | import androidx.annotation.UiThread; 12 | 13 | import java.util.HashMap; 14 | import java.util.HashSet; 15 | import java.util.Map; 16 | import java.util.Set; 17 | 18 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_MOVE; 19 | 20 | /** 21 | * Gesture detector handling move gesture. 22 | *
23 | * {@link MoveGestureDetector} serves similar purpose to
24 | * {@link com.mapbox.android.gestures.StandardGestureDetector.StandardOnGestureListener
25 | * #onScroll(MotionEvent, MotionEvent, float, float)}, however, it's a {@link ProgressiveGesture} that
26 | * introduces {@link OnMoveGestureListener#onMoveBegin(MoveGestureDetector)},
27 | * {@link OnMoveGestureListener#onMoveEnd(MoveGestureDetector, float, float)},
28 | * threshold with {@link MoveGestureDetector#setMoveThreshold(float)} and multi finger support thanks to
29 | * {@link MoveDistancesObject}.
30 | */
31 | @UiThread
32 | public class MoveGestureDetector extends ProgressiveGesture
224 | * We encourage to set those values from dimens to accommodate for various screen sizes.
225 | *
226 | * @param moveThreshold delta threshold
227 | * @see #setMoveThresholdRect(RectF)
228 | */
229 | public void setMoveThreshold(float moveThreshold) {
230 | this.moveThreshold = moveThreshold;
231 | }
232 |
233 | /**
234 | * Get the screen area in which the move gesture cannot be started.
235 | * If the gesture is already in progress, this value is ignored.
236 | * This condition is evaluated before {@link #setMoveThreshold(float)}.
237 | *
238 | * @return the screen area in which the gesture cannot be started
239 | */
240 | @Nullable
241 | public RectF getMoveThresholdRect() {
242 | return moveThresholdRect;
243 | }
244 |
245 | /**
246 | * Set the screen area in which the move gesture cannot be started.
247 | * If the gesture is already in progress, this value is ignored.
248 | * This condition is evaluated before {@link #setMoveThreshold(float)}.
249 | *
250 | * @param moveThresholdRect the screen area in which the gesture cannot be started
251 | */
252 | public void setMoveThresholdRect(@Nullable RectF moveThresholdRect) {
253 | this.moveThresholdRect = moveThresholdRect;
254 | }
255 |
256 | /**
257 | * Set the delta dp threshold required to qualify it as a move gesture.
258 | *
259 | * @param moveThresholdDimen delta threshold
260 | */
261 | public void setMoveThresholdResource(@DimenRes int moveThresholdDimen) {
262 | setMoveThreshold(context.getResources().getDimension(moveThresholdDimen));
263 | }
264 |
265 | /**
266 | * Returns X distance of the focal point in pixels
267 | * calculated during the last {@link OnMoveGestureListener#onMove(MoveGestureDetector, float, float)} call.
268 | *
269 | * @return X distance of the focal point in pixel
270 | */
271 | public float getLastDistanceX() {
272 | return lastDistanceX;
273 | }
274 |
275 | /**
276 | * Returns Y distance of the focal point in pixels
277 | * calculated during the last {@link OnMoveGestureListener#onMove(MoveGestureDetector, float, float)} call.
278 | *
279 | * @return Y distance of the focal point in pixel
280 | */
281 | public float getLastDistanceY() {
282 | return lastDistanceY;
283 | }
284 |
285 | /**
286 | * Returns {@link MoveDistancesObject} referencing the pointer held under passed index.
287 | *
288 | * Pointers are sorted by the time they were placed on the screen until lifted up.
289 | * This means that index 0 will reflect the oldest added, still active pointer
290 | * and index ({@link #getPointersCount()} - 1) will reflect the latest added, still active pointer.
291 | *
292 | *
293 | * @param pointerIndex pointer's index
294 | * @return distances object of the referenced pointer
295 | */
296 | public MoveDistancesObject getMoveObject(int pointerIndex) {
297 | if (isInProgress()) {
298 | if (pointerIndex >= 0 && pointerIndex < getPointersCount()) {
299 | return moveDistancesObjectMap.get(pointerIdList.get(pointerIndex));
300 | }
301 | }
302 | return null;
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/MultiFingerDistancesObject.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | /**
4 | * Object that holds pixel current and previous distances between a pair of fingers.
5 | */
6 | public class MultiFingerDistancesObject {
7 | private final float prevFingersDiffX;
8 | private final float prevFingersDiffY;
9 | private final float currFingersDiffX;
10 | private final float currFingersDiffY;
11 | private final float prevFingersDiffXY;
12 | private final float currFingersDiffXY;
13 |
14 | public MultiFingerDistancesObject(float prevFingersDiffX, float prevFingersDiffY,
15 | float currFingersDiffX, float currFingersDiffY) {
16 | this.prevFingersDiffX = prevFingersDiffX;
17 | this.prevFingersDiffY = prevFingersDiffY;
18 | this.currFingersDiffX = currFingersDiffX;
19 | this.currFingersDiffY = currFingersDiffY;
20 |
21 | prevFingersDiffXY =
22 | (float) Math.sqrt(prevFingersDiffX * prevFingersDiffX + prevFingersDiffY * prevFingersDiffY);
23 |
24 | currFingersDiffXY =
25 | (float) Math.sqrt(currFingersDiffX * currFingersDiffX + currFingersDiffY * currFingersDiffY);
26 | }
27 |
28 | public float getPrevFingersDiffX() {
29 | return prevFingersDiffX;
30 | }
31 |
32 | public float getPrevFingersDiffY() {
33 | return prevFingersDiffY;
34 | }
35 |
36 | public float getCurrFingersDiffX() {
37 | return currFingersDiffX;
38 | }
39 |
40 | public float getCurrFingersDiffY() {
41 | return currFingersDiffY;
42 | }
43 |
44 | public float getPrevFingersDiffXY() {
45 | return prevFingersDiffXY;
46 | }
47 |
48 | public float getCurrFingersDiffXY() {
49 | return currFingersDiffXY;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/MultiFingerTapGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.annotation.DimenRes;
6 | import androidx.annotation.UiThread;
7 | import androidx.annotation.NonNull;
8 |
9 | import android.view.MotionEvent;
10 |
11 | import java.util.HashMap;
12 |
13 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_MULTI_FINGER_TAP;
14 |
15 | /**
16 | * Gesture detector handling multi tap gesture.
17 | */
18 | @UiThread
19 | public class MultiFingerTapGestureDetector extends
20 | MultiFingerGesture
142 | * We encourage to set those values from dimens to accommodate for various screen sizes.
143 | *
144 | * @param multiFingerTapMovementThreshold movement threshold.
145 | */
146 | public void setMultiFingerTapMovementThreshold(float multiFingerTapMovementThreshold) {
147 | this.multiFingerTapMovementThreshold = multiFingerTapMovementThreshold;
148 | }
149 |
150 | /**
151 | * Set maximum movement allowed in dp for any finger before rejecting this gesture.
152 | *
153 | * @param multiFingerTapMovementThresholdDimen movement threshold.
154 | */
155 | public void setMultiFingerTapMovementThresholdResource(@DimenRes int multiFingerTapMovementThresholdDimen) {
156 | setMultiFingerTapMovementThreshold(context.getResources().getDimension(multiFingerTapMovementThresholdDimen));
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/PermittedActionsGuard.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | import androidx.annotation.IntRange;
4 | import android.view.MotionEvent;
5 |
6 | class PermittedActionsGuard {
7 |
8 | /**
9 | * Enables to store up to 8 permitted actions in the long type (64 bits / 8 bits per action).
10 | */
11 | private static final int BITS_PER_PERMITTED_ACTION = 8;
12 | private static final int PERMITTED_ACTION_MASK = ((1 << BITS_PER_PERMITTED_ACTION) - 1);
13 | private static final int NO_ACTION_PERMITTED = 0b11111111;
14 |
15 | boolean isMissingActions(int action,
16 | @IntRange(from = 0) int eventPointerCount,
17 | @IntRange(from = 0) int internalPointerCount) {
18 | long permittedActions = updatePermittedActions(eventPointerCount, internalPointerCount);
19 | if (action == permittedActions) {
20 | // this will only happen for action == permittedActions == ACTION_DOWN
21 | return false;
22 | }
23 |
24 | while (permittedActions != 0) {
25 | // get one of actions, the one on the first BITS_PER_PERMITTED_ACTION bits
26 | long testCase = permittedActions & PERMITTED_ACTION_MASK;
27 | if (action == testCase) {
28 | // we got a match, all good
29 | return false;
30 | }
31 |
32 | // remove the one we just checked and iterate
33 | permittedActions = permittedActions >> BITS_PER_PERMITTED_ACTION;
34 | }
35 |
36 | // no available matching actions, we are missing some!
37 | return true;
38 | }
39 |
40 | /**
41 | * Returns all acceptable at this point MotionEvent actions based on the pointers state.
42 | * Each one of them is written on {@link #BITS_PER_PERMITTED_ACTION} successive bits.
43 | */
44 | private long updatePermittedActions(@IntRange(from = 0) int eventPointerCount,
45 | @IntRange(from = 0) int internalPointerCount) {
46 | long permittedActions = MotionEvent.ACTION_DOWN;
47 |
48 | if (internalPointerCount == 0) {
49 | // only ACTION_DOWN available when no other pointers registered
50 | permittedActions = permittedActions << BITS_PER_PERMITTED_ACTION;
51 | permittedActions += MotionEvent.ACTION_DOWN;
52 | } else {
53 | if (Math.abs(eventPointerCount - internalPointerCount) > 1) {
54 | // missing a pointer up/down event, required to start over
55 | return NO_ACTION_PERMITTED;
56 | } else {
57 | if (eventPointerCount > internalPointerCount) {
58 | // event holds one more pointer than we have locally
59 | permittedActions = permittedActions << BITS_PER_PERMITTED_ACTION;
60 | permittedActions += MotionEvent.ACTION_POINTER_DOWN;
61 | } else if (eventPointerCount < internalPointerCount) {
62 | // event holds one less pointer than we have locally. This indicates that we are missing events,
63 | // because ACTION_UP and ACTION_POINTER_UP events still return not decremented pointer count
64 | return NO_ACTION_PERMITTED;
65 | } else {
66 | // event holds an equal number of pointers compared to the local count
67 | if (eventPointerCount == 1) {
68 | permittedActions = permittedActions << BITS_PER_PERMITTED_ACTION;
69 | permittedActions += MotionEvent.ACTION_UP;
70 | } else {
71 | permittedActions = permittedActions << BITS_PER_PERMITTED_ACTION;
72 | permittedActions += MotionEvent.ACTION_POINTER_UP;
73 | }
74 | permittedActions = permittedActions << BITS_PER_PERMITTED_ACTION;
75 | permittedActions += MotionEvent.ACTION_MOVE;
76 | }
77 | }
78 | }
79 |
80 | return permittedActions;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/PointerDistancePair.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | import android.util.Pair;
4 |
5 | public class PointerDistancePair extends Pair
187 | * We encourage to set those values from dimens to accommodate for various screen sizes.
188 | *
189 | * @param pixelDeltaThreshold delta threshold
190 | */
191 | public void setPixelDeltaThreshold(float pixelDeltaThreshold) {
192 | this.pixelDeltaThreshold = pixelDeltaThreshold;
193 | }
194 |
195 | /**
196 | * Set the delta dp threshold required to qualify it as a shove gesture.
197 | *
198 | * @param pixelDeltaThresholdDimen delta threshold
199 | */
200 | public void setPixelDeltaThresholdResource(@DimenRes int pixelDeltaThresholdDimen) {
201 | setPixelDeltaThreshold(context.getResources().getDimension(pixelDeltaThresholdDimen));
202 | }
203 |
204 | /**
205 | * Get the maximum allowed angle between fingers, measured from the horizontal line, to qualify it as a shove gesture.
206 | *
207 | * @return maximum allowed angle
208 | */
209 | public float getMaxShoveAngle() {
210 | return maxShoveAngle;
211 | }
212 |
213 | /**
214 | * Set the maximum allowed angle between fingers, measured from the horizontal line, to qualify it as a shove gesture.
215 | *
216 | * @param maxShoveAngle maximum allowed angle
217 | */
218 | public void setMaxShoveAngle(float maxShoveAngle) {
219 | this.maxShoveAngle = maxShoveAngle;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/SidewaysShoveGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.DimenRes;
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.UiThread;
7 |
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_SIDEWAYS_SHOVE;
12 |
13 | /**
14 | * Gesture detector handling sideways shove gesture.
15 | */
16 | @UiThread
17 | public class SidewaysShoveGestureDetector extends
18 | ProgressiveGesture
192 | * We encourage to set those values from dimens to accommodate for various screen sizes.
193 | *
194 | * @param pixelDeltaThreshold delta threshold
195 | */
196 | public void setPixelDeltaThreshold(float pixelDeltaThreshold) {
197 | this.pixelDeltaThreshold = pixelDeltaThreshold;
198 | }
199 |
200 | /**
201 | * Set the delta dp threshold required to qualify it as a sideways shove gesture.
202 | *
203 | * @param pixelDeltaThresholdDimen delta threshold
204 | */
205 | public void setPixelDeltaThresholdResource(@DimenRes int pixelDeltaThresholdDimen) {
206 | setPixelDeltaThreshold(context.getResources().getDimension(pixelDeltaThresholdDimen));
207 | }
208 |
209 | /**
210 | * Get the maximum allowed angle between fingers, measured from the vertical line,
211 | * to qualify it as a sideways shove gesture.
212 | *
213 | * @return maximum allowed angle
214 | */
215 | public float getMaxShoveAngle() {
216 | return maxShoveAngle;
217 | }
218 |
219 | /**
220 | * Set the maximum allowed angle between fingers, measured from the vertical line,
221 | * to qualify it as a sideways shove gesture.
222 | *
223 | * @param maxShoveAngle maximum allowed angle
224 | */
225 | public void setMaxShoveAngle(float maxShoveAngle) {
226 | this.maxShoveAngle = maxShoveAngle;
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/StandardGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.annotation.UiThread;
6 | import androidx.annotation.NonNull;
7 |
8 | import android.view.GestureDetector;
9 | import android.view.MotionEvent;
10 |
11 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_DOUBLE_TAP;
12 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_DOUBLE_TAP_EVENT;
13 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_DOWN;
14 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_FLING;
15 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS;
16 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_SCROLL;
17 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_SHOW_PRESS;
18 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_SINGLE_TAP_CONFIRMED;
19 | import static com.mapbox.android.gestures.AndroidGesturesManager.GESTURE_TYPE_SINGLE_TAP_UP;
20 |
21 | /**
22 | * Detector that wraps {@link GestureDetector}.
23 | */
24 | @UiThread
25 | public class StandardGestureDetector extends BaseGesture
255 | * We encourage to set those values from dimens to accommodate for various screen sizes.
256 | *
257 | * @param spanSinceStartThreshold delta span threshold
258 | */
259 | public void setSpanSinceStartThreshold(float spanSinceStartThreshold) {
260 | this.spanSinceStartThreshold = spanSinceStartThreshold;
261 | }
262 |
263 | /**
264 | * Set the threshold span in dp between initial fingers position and current needed
265 | * for this detector to qualify it as a scale gesture.
266 | *
267 | * @param spanSinceStartThresholdDimen delta span threshold
268 | */
269 | public void setSpanSinceStartThresholdResource(@DimenRes int spanSinceStartThresholdDimen) {
270 | setSpanSinceStartThreshold(context.getResources().getDimension(spanSinceStartThresholdDimen));
271 | }
272 |
273 | /**
274 | * @return Scale factor.
275 | */
276 | public float getScaleFactor() {
277 | return scaleFactor;
278 | }
279 |
280 | /**
281 | * Return the average distance between each of the pointers forming the
282 | * gesture in progress through the focal point, when the gesture was started.
283 | *
284 | * @return Distance between pointers in pixels.
285 | */
286 | public float getStartSpan() {
287 | return startSpan;
288 | }
289 |
290 | /**
291 | * Return the average X distance between each of the pointers forming the
292 | * gesture in progress through the focal point, when the gesture was started.
293 | *
294 | * @return Distance between pointers in pixels.
295 | */
296 | public float getStartSpanX() {
297 | return startSpanX;
298 | }
299 |
300 | /**
301 | * Return the average Y distance between each of the pointers forming the
302 | * gesture in progress through the focal point, when the gesture was started.
303 | *
304 | * @return Distance between pointers in pixels.
305 | */
306 | public float getStartSpanY() {
307 | return startSpanY;
308 | }
309 |
310 | /**
311 | * Return the average distance between each of the pointers forming the
312 | * gesture in progress through the focal point.
313 | *
314 | * @return Distance between pointers in pixels.
315 | */
316 | public float getCurrentSpan() {
317 | return currentSpan;
318 | }
319 |
320 | /**
321 | * Return the average X distance between each of the pointers forming the
322 | * gesture in progress through the focal point.
323 | *
324 | * @return Distance between pointers in pixels.
325 | */
326 | public float getCurrentSpanX() {
327 | return currentSpanX;
328 | }
329 |
330 | /**
331 | * Return the average Y distance between each of the pointers forming the
332 | * gesture in progress through the focal point.
333 | *
334 | * @return Distance between pointers in pixels.
335 | */
336 | public float getCurrentSpanY() {
337 | return currentSpanY;
338 | }
339 |
340 | /**
341 | * Return the previous average distance between each of the pointers forming the
342 | * gesture in progress through the focal point.
343 | *
344 | * @return Previous distance between pointers in pixels.
345 | */
346 | public float getPreviousSpan() {
347 | return previousSpan;
348 | }
349 |
350 | /**
351 | * Return the previous average X distance between each of the pointers forming the
352 | * gesture in progress through the focal point.
353 | *
354 | * @return Previous distance between pointers in pixels.
355 | */
356 | public float getPreviousSpanX() {
357 | return previousSpanX;
358 | }
359 |
360 | /**
361 | * Return the previous average Y distance between each of the pointers forming the
362 | * gesture in progress through the focal point.
363 | *
364 | * @return Previous distance between pointers in pixels.
365 | */
366 | public float getPreviousSpanY() {
367 | return previousSpanY;
368 | }
369 |
370 | private float calculateScaleFactor() {
371 | if (quickScale) {
372 | final boolean scaleOut =
373 | // below focal point moving up
374 | getCurrentEvent().getY() < quickScaleFocalPoint.y && currentSpan < previousSpan
375 | // above focal point moving up
376 | || getCurrentEvent().getY() > quickScaleFocalPoint.y && currentSpan > previousSpan;
377 | final float spanDiff = Math.abs(1 - (currentSpan / previousSpan)) * QUICK_SCALE_MULTIPLIER;
378 | return previousSpan <= 0 ? 1 : scaleOut ? (1 + spanDiff) : (1 - spanDiff);
379 | } else {
380 | return previousSpan > 0 ? currentSpan / previousSpan : 1;
381 | }
382 | }
383 | }
384 |
--------------------------------------------------------------------------------
/library/src/main/java/com/mapbox/android/gestures/Utils.java:
--------------------------------------------------------------------------------
1 | package com.mapbox.android.gestures;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.graphics.PointF;
6 | import androidx.annotation.NonNull;
7 | import android.util.DisplayMetrics;
8 | import android.util.TypedValue;
9 | import android.view.MotionEvent;
10 |
11 | public class Utils {
12 |
13 | /**
14 | * Calculates the center point of the multi finger gesture.
15 | *
16 | * @param motionEvent event
17 | * @return center point of the gesture
18 | */
19 | public static PointF determineFocalPoint(@NonNull MotionEvent motionEvent) {
20 | int pointersCount = motionEvent.getPointerCount();
21 | float x = 0;
22 | float y = 0;
23 |
24 | for (int i = 0; i < pointersCount; i++) {
25 | x += motionEvent.getX(i);
26 | y += motionEvent.getY(i);
27 | }
28 |
29 | return new PointF(x / pointersCount, y / pointersCount);
30 | }
31 |
32 | /**
33 | * @param event motion event
34 | * @param pointerIndex pointer's index
35 | * @return rawY for a pointer
36 | * @author Almer Thie (code.almeros.com)
37 | *
38 | * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
39 | */
40 | public static float getRawX(MotionEvent event, int pointerIndex) {
41 | float offset = event.getRawX() - event.getX();
42 | if (pointerIndex < event.getPointerCount()) {
43 | return event.getX(pointerIndex) + offset;
44 | }
45 | return 0.0f;
46 | }
47 |
48 | /**
49 | * @param event motion event
50 | * @param pointerIndex pointer's index
51 | * @return rawX for a pointer
52 | * @author Almer Thie (code.almeros.com)
53 | *
54 | * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
55 | */
56 | public static float getRawY(MotionEvent event, int pointerIndex) {
57 | float offset = event.getRawY() - event.getY();
58 | if (pointerIndex < event.getPointerCount()) {
59 | return event.getY(pointerIndex) + offset;
60 | }
61 | return 0.0f;
62 | }
63 |
64 | /**
65 | * Converts DIP to PX.
66 | *
67 | * @param dp initial value
68 | * @return converted value
69 | */
70 | public static float dpToPx(float dp) {
71 | return dp * Resources.getSystem().getDisplayMetrics().density;
72 | }
73 |
74 | /**
75 | * Converts PX to DIP.
76 | *
77 | * @param px initial value
78 | * @return converted value
79 | */
80 | public static float pxToDp(float px) {
81 | return px / Resources.getSystem().getDisplayMetrics().density;
82 | }
83 |
84 | /**
85 | * Converts PX to MM (millimeters).
86 | *
87 | * @param px initial value
88 | * @param context context
89 | * @return converted value
90 | */
91 | public static float pxToMm(final float px, final Context context) {
92 | final DisplayMetrics dm = context.getResources().getDisplayMetrics();
93 | return px / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1, dm);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |