Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gestures examples #189

Merged
merged 5 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,5 @@ dependencies {
classifier = "sources"
}
}
implementation libs.kotlin.reflect
}
28 changes: 19 additions & 9 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Expand All @@ -15,11 +15,19 @@
android:roundIcon="@mipmap/icon_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MutuallyExclusiveGesturesActivity"
android:exported="false" />
<activity
android:name=".GesturesMapPointActivity"
android:exported="false" />
<activity
android:name=".GesturesActivity"
android:exported="false" />
<activity
android:name=".SplashActivity"
android:exported="true"
android:theme="@style/SplashTheme"
>
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand All @@ -38,15 +46,17 @@
tools:ignore="LockedOrientationActivity" />
<activity
android:name=".MapStyleActivity"
android:theme="@style/MapStyleActivityTheme"
/>
android:theme="@style/MapStyleActivityTheme" />
<activity
android:name=".NavigationActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:theme="@style/GenericActivityTheme" />
<activity android:name=".MapFpsActivity" android:theme="@style/GenericActivityTheme"/>
<activity android:name=".ParkingActivity"
android:theme="@style/AppTheme"/>
<activity
android:name=".MapFpsActivity"
android:theme="@style/GenericActivityTheme" />
<activity
android:name=".ParkingActivity"
android:theme="@style/AppTheme" />
</application>

</manifest>
185 changes: 185 additions & 0 deletions app/src/main/java/ru/dgis/sdk/demo/GesturesActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package ru.dgis.sdk.demo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import ru.dgis.sdk.demo.common.addSettingsLayout
import ru.dgis.sdk.demo.databinding.ActivityGesturesBinding
import ru.dgis.sdk.demo.databinding.ActivityGesturesSettingsBinding
import ru.dgis.sdk.map.Gesture
import kotlin.reflect.KProperty1
import kotlin.reflect.full.functions
import kotlin.reflect.full.instanceParameter

/**
* Sample activity for demonstration of maps's Gesture Manager possibilities in terms of enabling / disabling gestures and modifying their settings
* For further info check [SDK Documentation](https://docs.2gis.com/en/android/sdk/reference/7.0/ru.dgis.sdk.map.GestureManager)
*
* Some reflection used here to reduce boilerplate code, see [readSettingsPropertyByName] and [copySettingsByName]
* Lets see on example:
*
* 1. readSettingsPropertyByName() is equivalent for property accessor in data class, hence
* settings = TiltSettings(...)
* assertEquals(settings.lenOnDegreeMm, readSettingsPropertyByName(settings, "lenOnDegreeMm")) // will pass
*
* 2. copySettingsByName() is equivalent for copy() function of data class, hence
* settings = TiltSettings(...)
* assertEquals(settings.copy(lenOnDegreeMm = 42.0), copySettingsByName((settings), mapOf("lenOnDegreeMm" to 42.0)) // will pass
*
* Be sure to not use this in production code because reflection is slow. Use strict data class methods instead.
*/
class GesturesActivity : AppCompatActivity() {
private val binding: ActivityGesturesBinding by lazy { ActivityGesturesBinding.inflate(layoutInflater) }
private val mapView by lazy { binding.mapView }
private val gestureManager by lazy { mapView.gestureManager }
private val settingsBinding by lazy { prepareSettingsBinding() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.addSettingsLayout().apply {
settingsDrawerInnerLayout.addView(settingsBinding.root)
}

/**
* Using hack here to delay settings initialization until map is ready and gestureManager is not null for sure
*/
mapView.getMapAsync {
initSwitches()
initRotationSettings()
initTiltSettings()
initScailingSettings()
initMultiTouchShiftSettings()
}
}

private fun prepareSettingsBinding(): ActivityGesturesSettingsBinding {
return ActivityGesturesSettingsBinding.inflate(layoutInflater)
}

private fun initSwitches() {
val gestureManager = this.gestureManager!!

mapOf(
settingsBinding.scailingSwitch to Gesture.SCALING,
settingsBinding.shiftSwitch to Gesture.SHIFT,
settingsBinding.multiTouchShiftSwitch to Gesture.MULTI_TOUCH_SHIFT,
settingsBinding.tiltSwitch to Gesture.TILT,
settingsBinding.rotationSwitch to Gesture.ROTATION
).forEach { (switchMaterial, gesture) ->
switchMaterial.isChecked = gestureManager.gestureEnabled(gesture)
switchMaterial.setOnCheckedChangeListener { _, isChecked ->
/**
* Here is main functions for enabling / disabling gestures
*/
if (isChecked) {
gestureManager.enableGesture(gesture)
} else {
gestureManager.disableGesture(gesture)
}
}
}
}

private fun initRotationSettings() {
val gestureManager = this.gestureManager!!
val rotationSettings = gestureManager.rotationSettings

mapOf(
settingsBinding.rotationAnglediffinscalingdegEditText to "angleDiffInScalingDeg",
settingsBinding.rotationDistancediffmmEditText to "distanceDiffMm",
settingsBinding.rotationAnglediffdegEditText to "angleDiffDeg",
Sameri11 marked this conversation as resolved.
Show resolved Hide resolved
settingsBinding.rotationDistancediffinscalingmmEditText to "distanceDiffInScalingMm"
).forEach { (editText, setting) ->
val propertyValue = readSettingsPropertyByName<Float>(rotationSettings, setting)
editText.apply {
setText(propertyValue.toString())
addTextChangedListener {
gestureManager.rotationSettings = copySettingsByName(
gestureManager.rotationSettings,
mapOf(setting to it.toString().toFloat())
)
}
}
}
}

private fun initTiltSettings() {
val gestureManager = this.gestureManager!!
val tiltSettings = gestureManager.tiltSettings

mapOf(
settingsBinding.tiltHorizontalswervedegEditText to "horizontalSwerveDeg",
settingsBinding.tiltLenondegreemmEditText to "lenOnDegreeMm",
settingsBinding.tiltThresholdmmEditText to "thresholdMm",
settingsBinding.tiltVerticalswervedegEditText to "verticalSwerveDeg"
).forEach { (editText, setting) ->
val propertyValue = readSettingsPropertyByName<Float>(tiltSettings, setting)
editText.apply {
setText(propertyValue.toString())
addTextChangedListener {
gestureManager.tiltSettings = copySettingsByName(
gestureManager.tiltSettings,
mapOf(setting to it.toString().toFloat())
)
}
}
}
}

private fun initScailingSettings() {
val gestureManager = this.gestureManager!!
val scalingSettings = gestureManager.scalingSettings

mapOf(
settingsBinding.scailingScaleratiothresholdEditText to "scaleRatioThreshold",
settingsBinding.scailingScaleratiothresholdinrotationEditText to "scaleRatioThresholdInRotation"
).forEach { (editText, setting) ->
val propertyValue = readSettingsPropertyByName<Float>(scalingSettings, setting)
editText.apply {
setText(propertyValue.toString())
addTextChangedListener {
gestureManager.scalingSettings = copySettingsByName(
gestureManager.scalingSettings,
mapOf(setting to it.toString().toFloat())
)
}
}
}
}
private fun initMultiTouchShiftSettings() {
val gestureManager = this.gestureManager!!
val multiTouchShiftSettings = gestureManager.multitouchShiftSettings

mapOf(
settingsBinding.multiTouchShiftThresholdmmEditText to "thresholdMm"
).forEach { (editText, setting) ->
val propertyValue = readSettingsPropertyByName<Float>(multiTouchShiftSettings, setting)
editText.apply {
setText(propertyValue.toString())
addTextChangedListener {
gestureManager.multitouchShiftSettings = copySettingsByName(
gestureManager.multitouchShiftSettings,
mapOf(setting to it.toString().toFloat())
)
}
}
}
}

@Suppress("UNCHECKED_CAST")
private fun <T> readSettingsPropertyByName(instance: Any, propertyName: String): T {
val property = instance::class.members.first { it.name == propertyName } as KProperty1<Any, *>
return property.get(instance) as T
}

@Suppress("UNCHECKED_CAST")
private fun <T : Any> copySettingsByName(instance: T, newValues: Map<String, Any>): T {
val clazz = instance::class
val copyFunction = clazz.functions.first { it.name == "copy" }
val args = copyFunction.parameters
.filter { param -> newValues.keys.contains(param.name) }
.map { param -> param to newValues[param.name] }

return copyFunction.callBy(mapOf(copyFunction.instanceParameter!! to instance) + args) as? T ?: error("error")
}
}
72 changes: 72 additions & 0 deletions app/src/main/java/ru/dgis/sdk/demo/GesturesMapPointActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package ru.dgis.sdk.demo

import android.os.Bundle
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.doAfterTextChanged
import ru.dgis.sdk.demo.common.addSettingsLayout
import ru.dgis.sdk.demo.databinding.ActivityGesturesBinding
import ru.dgis.sdk.demo.databinding.ActivityGesturesMapPointSettingsBinding
import ru.dgis.sdk.map.EventsProcessingSettings
import ru.dgis.sdk.map.RotationCenter
import ru.dgis.sdk.map.ScalingCenter

/**
* Sample activity for demonstration of maps's Gesture Manager possibilities in terms of setting map point, which gestures will be relative to
* It's hard to test these cases on an emulator since all multitouch gestures will use center of screen as a center of segment between 2 touch points,
* so we recommend to use real smartphone here.
*
* For further details check [SDK Documentation](https://docs.2gis.com/en/android/sdk/reference/7.0/ru.dgis.sdk.map.GestureManager#nav-lvl1--setSettingsAboutMapPositionPoint)
*/
class GesturesMapPointActivity : AppCompatActivity() {

private val binding by lazy { ActivityGesturesBinding.inflate(layoutInflater) }
private val mapView by lazy { binding.mapView }
private val gestureManager by lazy { mapView.gestureManager }
private val settingsBinding by lazy { prepareSettingsBinding() }
private var eventProcessingSettings = EventsProcessingSettings(
RotationCenter.MAP_POSITION,
ScalingCenter.MAP_POSITION
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.addSettingsLayout().apply {
settingsDrawerInnerLayout.addView(settingsBinding.root)
}

/**
* Using hack here to delay settings initialization until map is ready and gestureManager is not null for sure
*/
mapView.getMapAsync {
initSettings()
}
}

private fun prepareSettingsBinding(): ActivityGesturesMapPointSettingsBinding {
return ActivityGesturesMapPointSettingsBinding.inflate(layoutInflater).apply {
val options = resources.getStringArray(R.array.events_processing_settings)
val adapter = ArrayAdapter(this@GesturesMapPointActivity, R.layout.dropdown_item, options)
rotationCenterTextView.setAdapter(adapter)
scailingCenterTextView.setAdapter(adapter)
}
}

private fun initSettings() {
val gestureManager = this.gestureManager!!

settingsBinding.rotationCenterTextView.setText(eventProcessingSettings.rotationCenter.toString(), false)
settingsBinding.rotationCenterTextView.doAfterTextChanged { editable ->
val newRotationCenter = RotationCenter.entries.first { it.name == editable.toString() }
eventProcessingSettings = eventProcessingSettings.copy(rotationCenter = newRotationCenter)
gestureManager.setSettingsAboutMapPositionPoint(eventProcessingSettings)
}

settingsBinding.scailingCenterTextView.setText(eventProcessingSettings.scalingCenter.toString(), false)
settingsBinding.scailingCenterTextView.doAfterTextChanged { editable ->
val newScailingCenter = ScalingCenter.entries.first { it.name == editable.toString() }
eventProcessingSettings = eventProcessingSettings.copy(scalingCenter = newScailingCenter)
gestureManager.setSettingsAboutMapPositionPoint(eventProcessingSettings)
}
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ class MainActivity : AppCompatActivity() {
Page("Parkings on map") {
val intent = Intent(this@MainActivity, ParkingActivity::class.java)
startActivity(intent)
},
Page("Gestures") {
val intent = Intent(this@MainActivity, GesturesActivity::class.java)
startActivity(intent)
},
Page("Gestures: center point") {
val intent = Intent(this@MainActivity, GesturesMapPointActivity::class.java)
startActivity(intent)
},
Page("Mutually exclusive gestures") {
val intent = Intent(this@MainActivity, MutuallyExclusiveGesturesActivity::class.java)
startActivity(intent)
}
)

Expand Down
Loading