Skip to content

Commit

Permalink
Add gestures examples (#189)
Browse files Browse the repository at this point in the history
* update styles

* add gestures example

* add gestures map point example

* add mutually exclusive gestures example

* review fixes
  • Loading branch information
Sameri11 authored Aug 4, 2023
1 parent d446205 commit 97eb36d
Show file tree
Hide file tree
Showing 16 changed files with 980 additions and 15 deletions.
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",
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

0 comments on commit 97eb36d

Please sign in to comment.