Created
August 21, 2020 16:36
-
-
Save kustraslawomir/d92cbca2abdb6bf3c26a38ff0717a973 to your computer and use it in GitHub Desktop.
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
package com.ultron.ar.presentation.argameplay | |
import android.Manifest | |
import android.os.Bundle | |
import android.view.View | |
import android.widget.TextView | |
import com.domain.model.jobdetails.JobDetailsDomain | |
import com.domain.model.route.Type | |
import com.domain.usecase.games.SubmitAnswerUseCase | |
import com.google.ar.core.Config | |
import com.google.ar.core.TrackingState | |
import com.google.ar.sceneform.FrameTime | |
import com.google.ar.sceneform.Scene | |
import com.karumi.dexter.Dexter | |
import com.karumi.dexter.MultiplePermissionsReport | |
import com.karumi.dexter.PermissionToken | |
import com.karumi.dexter.listener.PermissionRequest | |
import com.karumi.dexter.listener.multi.MultiplePermissionsListener | |
import com.ultron.ar.arlocation.extensions.extensions.ArSceneLifecycleUtil | |
import com.ultron.ar.arlocation.extensions.extensions.GlobalPosition | |
import com.ultron.ar.arlocation.extensions.extensions.LocationArScene | |
import com.ultron.ar.arlocation.extensions.model.NavigationMarker | |
import com.ultron.ar.presentation.BaseFragment | |
import com.ultron.ar.presentation.R | |
import com.ultron.ar.presentation.argameplay.viewmodels.ArViewModel | |
import com.ultron.ar.presentation.argameplay.viewmodels.ObjectRecognizeViewModel | |
import com.ultron.ar.presentation.argameplay.viewmodels.QrRecognizeViewModel | |
import com.ultron.ar.presentation.argameplay.viewmodels.TextRecognizerViewModel | |
import com.ultron.ar.presentation.gameplay.GamePlayUiState | |
import com.ultron.ar.presentation.gameplay.GamePlayViewModel | |
import com.ultron.ar.utils.extensions.toJson | |
import kotlinx.android.synthetic.main.fragment_ar_gameplay.* | |
import kotlinx.coroutines.Dispatchers | |
import kotlinx.coroutines.GlobalScope | |
import kotlinx.coroutines.launch | |
import utils.* | |
import java.util.* | |
const val JOB_DETAILS = "job_details" | |
open class ArGamePlayFragment : BaseFragment(), Scene.OnUpdateListener { | |
private lateinit var arLifeCycle: ArSceneLifecycleUtil | |
private lateinit var locationArScene: LocationArScene | |
private lateinit var jobDetailsDomain: JobDetailsDomain | |
private var currentTrackingState: TrackingState = TrackingState.STOPPED | |
private val gamePlayViewModel: GamePlayViewModel by lazy { | |
getViewModel(getNavigationActivity(), GamePlayViewModel::class.java) | |
} | |
private val arViewModel: ArViewModel by lazy { | |
getViewModel(getNavigationActivity(), ArViewModel::class.java) | |
} | |
private val objectRecognizeViewModel: ObjectRecognizeViewModel by lazy { | |
getViewModel(this, ObjectRecognizeViewModel::class.java) | |
} | |
private val qrRecognizeViewModel: QrRecognizeViewModel by lazy { | |
getViewModel(this, QrRecognizeViewModel::class.java) | |
} | |
private val textRecognizerViewModel: TextRecognizerViewModel by lazy { | |
getViewModel(this, TextRecognizerViewModel::class.java) | |
} | |
companion object { | |
fun getInstance(jobDetails: JobDetailsDomain) = | |
ArGamePlayFragment().apply { | |
arguments = Bundle().apply { | |
putParcelable(JOB_DETAILS, jobDetails) | |
} | |
} | |
} | |
override fun setLayout() = R.layout.fragment_ar_gameplay | |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
super.onViewCreated(view, savedInstanceState) | |
jobDetailsDomain = arguments?.getParcelable<JobDetailsDomain>(JOB_DETAILS) as JobDetailsDomain | |
sceneView.session?.configure(sceneView.session?.config?.apply { | |
focusMode = Config.FocusMode.AUTO | |
}) | |
gamePlayViewModel.getGamePlayUiState().listen(viewLifecycleOwner) { state -> | |
if (state is GamePlayUiState.Execution) { | |
listenToDetectorsAnswers(jobDetailsDomain.type) | |
scanView.show() | |
north.hide() | |
} | |
} | |
checkPermissions() | |
} | |
override fun onResume() { | |
super.onResume() | |
if (::arLifeCycle.isInitialized) | |
arLifeCycle.onActivityResume() | |
} | |
override fun onPause() { | |
super.onPause() | |
if (::arLifeCycle.isInitialized) | |
arLifeCycle.onActivityPause() | |
} | |
override fun onDestroy() { | |
super.onDestroy() | |
if (::arLifeCycle.isInitialized) | |
arLifeCycle.onActivityDestroy() | |
} | |
override fun onUpdate(frame: FrameTime?) { | |
checkTrackingState(trackingState = sceneView.arFrame?.camera?.trackingState | |
?: TrackingState.STOPPED) | |
updateArMarkersUi() | |
invalidateDetector() | |
} | |
private fun invalidateDetector() { | |
if (arViewModel.shouldProcessFrame()) { | |
GlobalScope.launch(Dispatchers.IO) { | |
when (jobDetailsDomain.type) { | |
JobDetailsDomain.FIND_OBJECT_JOB -> objectRecognizeViewModel.recognizeObject(sceneView.arFrame) | |
JobDetailsDomain.TEXT_ANSWER_JOB -> textRecognizerViewModel.recognizeText(sceneView.arFrame) | |
JobDetailsDomain.QR_JOB -> qrRecognizeViewModel.recognizeQrCode(sceneView.arFrame) | |
} | |
} | |
} | |
} | |
private fun listenToDetectorsAnswers(jobType: String) { | |
val answer = jobDetailsDomain.answer | |
when (jobType) { | |
JobDetailsDomain.FIND_OBJECT_JOB -> listenToObjectRecognizeResults(answer) | |
JobDetailsDomain.TEXT_ANSWER_JOB -> listenToTextRecognizeResults(answer) | |
JobDetailsDomain.QR_JOB -> listenToQrScanResults(answer) | |
} | |
} | |
private fun listenToObjectRecognizeResults(answer: String) { | |
objectRecognizeViewModel.debugTestModel() | |
objectRecognizeViewModel.listenToResults().listen(viewLifecycleOwner) { results -> | |
results.forEach { result -> | |
gamePlayViewModel.showSuccessDialog(result.toJson()) | |
if (result.title.toLowerCase(Locale.getDefault()) | |
.contains(answer.toLowerCase(Locale.getDefault()))) { | |
gamePlayViewModel.submitAnswer(SubmitAnswerUseCase.getFindObjectAnswer( | |
isCorrect = true, | |
score = result.confidence.toDouble() | |
)) | |
} | |
} | |
} | |
} | |
private fun listenToTextRecognizeResults(answer: String) { | |
textRecognizerViewModel.listenToResults().listen(viewLifecycleOwner) { result -> | |
gamePlayViewModel.showSuccessDialog(result.toJson()) | |
if (result.toLowerCase(Locale.getDefault()).contains(answer.toLowerCase(Locale.getDefault()))) { | |
gamePlayViewModel.submitAnswer(SubmitAnswerUseCase.getTextRecognizeAnswer( | |
text = answer | |
)) | |
} | |
} | |
} | |
private fun listenToQrScanResults(answer: String) { | |
qrRecognizeViewModel.listenToResults().listen(viewLifecycleOwner) { result -> | |
gamePlayViewModel.showSuccessDialog(result.toJson()) | |
if (result.contains(answer)) { | |
gamePlayViewModel.submitAnswer(SubmitAnswerUseCase.getQrScanAnswer( | |
qrCodeText = answer)) | |
} | |
} | |
} | |
private fun checkTrackingState(trackingState: TrackingState) { | |
if (currentTrackingState != trackingState) { | |
currentTrackingState = trackingState | |
when (currentTrackingState) { | |
TrackingState.PAUSED, | |
TrackingState.STOPPED | |
-> locationArScene.clearMarkers() | |
TrackingState.TRACKING -> { | |
arViewModel.getCurrentNavigationMarker().value?.let { markers -> | |
locationArScene.clearMarkers() | |
markers.forEach { marker -> | |
renderNavigationMarker(marker) | |
} | |
} | |
} | |
} | |
} | |
} | |
private fun updateArMarkersUi() { | |
if (arViewModel.shouldUpdateRenderable()) { | |
val markers = arViewModel.getCurrentNavigationMarker().value | |
if (markers.isNullOrEmpty()) { | |
return | |
} | |
markers.forEach { marker -> | |
setMarkerUi(marker) | |
} | |
} | |
} | |
private fun setLocationListener() { | |
arViewModel.getLocationLiveData().listen(viewLifecycleOwner) { location -> | |
if (::locationArScene.isInitialized) | |
locationArScene.onLocationChanged( | |
newLocation = GlobalPosition(location.latitude, location.longitude)) | |
} | |
} | |
private fun setBearingListener() { | |
arViewModel.getBearing().listen(viewLifecycleOwner) { bearing -> | |
north.rotation = bearing.azimuth | |
if (::locationArScene.isInitialized) | |
locationArScene.onBearingChanged(bearing.azimuth) | |
} | |
} | |
private fun renderNavigationMarker(navigationMarker: NavigationMarker) { | |
TimberLog.d("load view renderable: %s ", navigationMarker.title) | |
arViewModel.loadViewRenderable(getNavigationActivity(), navigationMarker.viewId, { renderable -> | |
val marker = arViewModel.renderArObject( | |
navigationMarker.latitude, | |
navigationMarker.longitude, | |
navigationMarker.height, | |
renderable) | |
navigationMarker.viewRenderable = renderable | |
locationArScene.addMarker(marker) | |
setMarkerUi(navigationMarker) | |
}, { error -> TimberLog.e("Error while loading view renderables: %s", error.message) }) | |
} | |
private fun setMarkerUi(navigationMarker: NavigationMarker) { | |
when (navigationMarker.type) { | |
is Type.RouteStep -> { | |
navigationMarker.viewRenderable?.view?.findViewById<TextView>(R.id.title).apply { | |
this?.text = navigationMarker.title | |
} | |
navigationMarker.viewRenderable?.view?.findViewById<TextView>(R.id.distance).apply { | |
this?.text = arViewModel.getDistanceToMarker(navigationMarker.latitude, navigationMarker.longitude) | |
} | |
} | |
} | |
} | |
private fun checkPermissions() { | |
Dexter.withContext(getNavigationActivity()) | |
.withPermissions( | |
Manifest.permission.CAMERA, | |
Manifest.permission.ACCESS_FINE_LOCATION, | |
Manifest.permission.ACCESS_COARSE_LOCATION | |
).withListener(object : MultiplePermissionsListener { | |
override fun onPermissionsChecked(report: MultiplePermissionsReport) { | |
TimberLog.d("Permissions granted.") | |
if (report.areAllPermissionsGranted()) { | |
arLifeCycle = ArSceneLifecycleUtil(getNavigationActivity(), sceneView) | |
locationArScene = LocationArScene(sceneView).apply { | |
startLocationScene() | |
} | |
setLocationListener() | |
setBearingListener() | |
setOnUpdateListener() | |
listenToMarkersChange() | |
} | |
} | |
override fun onPermissionRationaleShouldBeShown(permissions: List<PermissionRequest?>?, token: PermissionToken?) { | |
} | |
}).check() | |
} | |
private fun listenToMarkersChange() { | |
arViewModel.getCurrentNavigationMarker().listen(viewLifecycleOwner) { markers -> | |
locationArScene.clearMarkers() | |
markers.forEach { marker -> | |
renderNavigationMarker(marker) | |
} | |
} | |
} | |
private fun setOnUpdateListener() = sceneView | |
.scene | |
.addOnUpdateListener(this) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment