move to firestore
This commit is contained in:
parent
1433334f15
commit
9a0a49fd38
6
cloud-anchors/.idea/vcs.xml
Normal file
6
cloud-anchors/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -37,7 +37,7 @@ dependencies {
|
|||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||||
implementation "com.google.ar.sceneform.ux:sceneform-ux:1.6.0"
|
implementation "com.google.ar.sceneform.ux:sceneform-ux:1.6.0"
|
||||||
implementation 'com.google.firebase:firebase-core:16.0.6'
|
implementation 'com.google.firebase:firebase-core:16.0.6'
|
||||||
implementation 'com.google.firebase:firebase-database:16.0.5'
|
implementation 'com.google.firebase:firebase-firestore:17.1.5'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, norangebit
|
||||||
|
*
|
||||||
|
* This file is part of cloud-anchors.
|
||||||
|
*
|
||||||
|
* cloud-anchors is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* cloud-anchors is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with cloud-anchors. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package it.norangeb.cloudanchors
|
||||||
|
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore
|
||||||
|
import com.google.firebase.firestore.SetOptions
|
||||||
|
|
||||||
|
class CloudAnchorsHelper {
|
||||||
|
private val fireStoreDb = FirebaseFirestore.getInstance()
|
||||||
|
private var nextCode = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
fireStoreDb.collection(COLLECTION)
|
||||||
|
.document(DOCUMENT)
|
||||||
|
.get()
|
||||||
|
.addOnSuccessListener {
|
||||||
|
val nCode = it.data?.get(NEXT_CODE) ?: return@addOnSuccessListener
|
||||||
|
nextCode = nCode.toString().toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getShortCode(cloudAnchorId: String): Int {
|
||||||
|
fireStoreDb.collection(COLLECTION)
|
||||||
|
.document(DOCUMENT)
|
||||||
|
.set(
|
||||||
|
mapOf(Pair(nextCode.toString(), cloudAnchorId)),
|
||||||
|
SetOptions.merge())
|
||||||
|
uploadNextCode(nextCode+1)
|
||||||
|
return nextCode++
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun uploadNextCode(nextCode: Int) {
|
||||||
|
fireStoreDb.collection(COLLECTION)
|
||||||
|
.document(DOCUMENT)
|
||||||
|
.set(
|
||||||
|
mapOf(Pair(NEXT_CODE, nextCode)),
|
||||||
|
SetOptions.merge()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCloudAnchorId(
|
||||||
|
shortCode: Int,
|
||||||
|
onSuccess: (String) -> Unit
|
||||||
|
) {
|
||||||
|
fireStoreDb.collection(COLLECTION)
|
||||||
|
.document(DOCUMENT)
|
||||||
|
.get()
|
||||||
|
.addOnSuccessListener {
|
||||||
|
val x = it.data?.get(shortCode.toString()) as String
|
||||||
|
onSuccess(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val COLLECTION = "short_codes"
|
||||||
|
private const val DOCUMENT = "short_codes_doc"
|
||||||
|
private const val NEXT_CODE = "next_short_code"
|
||||||
|
}
|
||||||
|
}
|
@ -36,8 +36,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private val TAG = MainActivity::class.java.canonicalName
|
private val TAG = MainActivity::class.java.canonicalName
|
||||||
|
|
||||||
private lateinit var arFragment: CloudArFragment
|
private lateinit var arFragment: CloudArFragment
|
||||||
private lateinit var storageManager: StorageManager
|
private val cloudAnchorsHelper = CloudAnchorsHelper()
|
||||||
private var cloudAnchorState = CloudAnchorState.LOCAL
|
private var cloudAnchorState = CloudAnchorState.NONE
|
||||||
private var cloudAnchor: Anchor? = null
|
private var cloudAnchor: Anchor? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field?.detach()
|
field?.detach()
|
||||||
@ -50,8 +50,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (!checkIsSupportedDeviceOrFinish(this, TAG))
|
if (!checkIsSupportedDeviceOrFinish(this, TAG))
|
||||||
return
|
return
|
||||||
|
|
||||||
storageManager = StorageManager(this)
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
arFragment = supportFragmentManager.findFragmentById(R.id.ar_fragment) as CloudArFragment
|
arFragment = supportFragmentManager.findFragmentById(R.id.ar_fragment) as CloudArFragment
|
||||||
@ -63,7 +61,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addModel(hitResult: HitResult, plane: Plane, motionEvent: MotionEvent) {
|
private fun addModel(hitResult: HitResult, plane: Plane, motionEvent: MotionEvent) {
|
||||||
if (cloudAnchorState != CloudAnchorState.LOCAL)
|
if (cloudAnchorState != CloudAnchorState.NONE)
|
||||||
return
|
return
|
||||||
|
|
||||||
cloudAnchor = arFragment.arSceneView.session
|
cloudAnchor = arFragment.arSceneView.session
|
||||||
@ -90,7 +88,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
if (cloudState.isError) {
|
if (cloudState.isError) {
|
||||||
toastError()
|
toastError()
|
||||||
cloudAnchorState = CloudAnchorState.LOCAL
|
cloudAnchorState = CloudAnchorState.NONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,29 +96,25 @@ class MainActivity : AppCompatActivity() {
|
|||||||
return
|
return
|
||||||
|
|
||||||
if (cloudAnchorState == CloudAnchorState.HOSTING)
|
if (cloudAnchorState == CloudAnchorState.HOSTING)
|
||||||
checkHosting(cloudState)
|
checkHosting()
|
||||||
else
|
else
|
||||||
checkResolving(cloudState)
|
checkResolving()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkResolving(state: Anchor.CloudAnchorState) {
|
private fun checkResolving() {
|
||||||
Toast.makeText(this, "Anchor resolved!", Toast.LENGTH_LONG)
|
Toast.makeText(this, "Anchor resolved!", Toast.LENGTH_LONG)
|
||||||
.show()
|
.show()
|
||||||
cloudAnchorState = CloudAnchorState.RESOLVED
|
cloudAnchorState = CloudAnchorState.RESOLVED
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkHosting(state: Anchor.CloudAnchorState) {
|
private fun checkHosting() {
|
||||||
storageManager.nextShortCode { shortCode ->
|
val cAnchor = cloudAnchor ?: return
|
||||||
if (shortCode == null) {
|
|
||||||
toastError()
|
val shortCode = cloudAnchorsHelper.getShortCode(cAnchor.cloudAnchorId)
|
||||||
return@nextShortCode
|
|
||||||
}
|
|
||||||
storageManager.storeUsingShortCode(shortCode, cloudAnchor?.cloudAnchorId)
|
|
||||||
|
|
||||||
Toast.makeText(this, "Anchor hosted with code $shortCode", Toast.LENGTH_LONG)
|
Toast.makeText(this, "Anchor hosted with code $shortCode", Toast.LENGTH_LONG)
|
||||||
.show()
|
.show()
|
||||||
Log.d("NORANGEBIT", "$shortCode")
|
Log.d("NORANGEBIT", "$shortCode")
|
||||||
}
|
|
||||||
|
|
||||||
cloudAnchorState = CloudAnchorState.HOSTED
|
cloudAnchorState = CloudAnchorState.HOSTED
|
||||||
}
|
}
|
||||||
@ -130,8 +124,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
.show()
|
.show()
|
||||||
|
|
||||||
private fun onResolveOkPressed(dialogValue: String) {
|
private fun onResolveOkPressed(dialogValue: String) {
|
||||||
val shortCode = Integer.parseInt(dialogValue)
|
val shortCode = dialogValue.toInt()
|
||||||
storageManager.getCloudAnchorID(shortCode) {
|
cloudAnchorsHelper.getCloudAnchorId(shortCode) {
|
||||||
cloudAnchor = arFragment.arSceneView.session
|
cloudAnchor = arFragment.arSceneView.session
|
||||||
.resolveCloudAnchor(it)
|
.resolveCloudAnchor(it)
|
||||||
|
|
||||||
@ -146,7 +140,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
fun onClearClick(view: View) {
|
fun onClearClick(view: View) {
|
||||||
cloudAnchor?.detach()
|
cloudAnchor?.detach()
|
||||||
cloudAnchor = null
|
cloudAnchor = null
|
||||||
cloudAnchorState = CloudAnchorState.LOCAL
|
cloudAnchorState = CloudAnchorState.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onResolveClick(view: View) {
|
fun onResolveClick(view: View) {
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
/**
|
|
||||||
* This class is made by Google
|
|
||||||
*
|
|
||||||
* visit https://codelabs.developers.google.com/codelabs/arcore-cloud-anchors/#4
|
|
||||||
*/
|
|
||||||
|
|
||||||
package it.norangeb.cloudanchors;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.google.firebase.FirebaseApp;
|
|
||||||
import com.google.firebase.database.*;
|
|
||||||
|
|
||||||
/** Helper class for Firebase storage of cloud anchor IDs. */
|
|
||||||
class StorageManager {
|
|
||||||
|
|
||||||
/** Listener for a new Cloud Anchor ID from the Firebase Database. */
|
|
||||||
interface CloudAnchorIdListener {
|
|
||||||
void onCloudAnchorIdAvailable(String cloudAnchorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Listener for a new short code from the Firebase Database. */
|
|
||||||
interface ShortCodeListener {
|
|
||||||
void onShortCodeAvailable(Integer shortCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String TAG = StorageManager.class.getName();
|
|
||||||
private static final String KEY_ROOT_DIR = "shared_anchor_codelab_root";
|
|
||||||
private static final String KEY_NEXT_SHORT_CODE = "next_short_code";
|
|
||||||
private static final String KEY_PREFIX = "anchor;";
|
|
||||||
private static final int INITIAL_SHORT_CODE = 142;
|
|
||||||
private final DatabaseReference rootRef;
|
|
||||||
|
|
||||||
StorageManager(Context context) {
|
|
||||||
FirebaseApp firebaseApp = FirebaseApp.initializeApp(context);
|
|
||||||
rootRef = FirebaseDatabase.getInstance(firebaseApp).getReference().child(KEY_ROOT_DIR);
|
|
||||||
DatabaseReference.goOnline();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a new short code that can be used to store the anchor ID. */
|
|
||||||
void nextShortCode(ShortCodeListener listener) {
|
|
||||||
// Run a transaction on the node containing the next short code available. This increments the
|
|
||||||
// value in the database and retrieves it in one atomic all-or-nothing operation.
|
|
||||||
rootRef
|
|
||||||
.child(KEY_NEXT_SHORT_CODE)
|
|
||||||
.runTransaction(
|
|
||||||
new Transaction.Handler() {
|
|
||||||
@Override
|
|
||||||
public Transaction.Result doTransaction(MutableData currentData) {
|
|
||||||
Integer shortCode = currentData.getValue(Integer.class);
|
|
||||||
if (shortCode == null) {
|
|
||||||
shortCode = INITIAL_SHORT_CODE - 1;
|
|
||||||
}
|
|
||||||
currentData.setValue(shortCode + 1);
|
|
||||||
return Transaction.success(currentData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onComplete(
|
|
||||||
DatabaseError error, boolean committed, DataSnapshot currentData) {
|
|
||||||
if (!committed) {
|
|
||||||
Log.e(TAG, "Firebase Error", error.toException());
|
|
||||||
listener.onShortCodeAvailable(null);
|
|
||||||
} else {
|
|
||||||
listener.onShortCodeAvailable(currentData.getValue(Integer.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Stores the cloud anchor ID in the configured Firebase Database. */
|
|
||||||
void storeUsingShortCode(int shortCode, String cloudAnchorId) {
|
|
||||||
rootRef.child(KEY_PREFIX + shortCode).setValue(cloudAnchorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the cloud anchor ID using a short code. Returns an empty string if a cloud anchor ID
|
|
||||||
* was not stored for this short code.
|
|
||||||
*/
|
|
||||||
void getCloudAnchorID(int shortCode, CloudAnchorIdListener listener) {
|
|
||||||
rootRef
|
|
||||||
.child(KEY_PREFIX + shortCode)
|
|
||||||
.addListenerForSingleValueEvent(
|
|
||||||
new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(DataSnapshot dataSnapshot) {
|
|
||||||
listener.onCloudAnchorIdAvailable(String.valueOf(dataSnapshot.getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(DatabaseError error) {
|
|
||||||
Log.e(TAG, "The database operation for getCloudAnchorID was cancelled.",
|
|
||||||
error.toException());
|
|
||||||
listener.onCloudAnchorIdAvailable(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,12 +29,8 @@ import android.os.Build
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.google.ar.core.Anchor
|
import com.google.ar.core.Anchor
|
||||||
import com.google.ar.core.Session
|
|
||||||
import com.google.ar.core.Trackable
|
|
||||||
import com.google.ar.core.TrackingState
|
|
||||||
import com.google.ar.sceneform.AnchorNode
|
import com.google.ar.sceneform.AnchorNode
|
||||||
import com.google.ar.sceneform.Node
|
import com.google.ar.sceneform.Node
|
||||||
import com.google.ar.sceneform.math.Vector3
|
|
||||||
import com.google.ar.sceneform.rendering.*
|
import com.google.ar.sceneform.rendering.*
|
||||||
import com.google.ar.sceneform.ux.ArFragment
|
import com.google.ar.sceneform.ux.ArFragment
|
||||||
import com.google.ar.sceneform.ux.TransformableNode
|
import com.google.ar.sceneform.ux.TransformableNode
|
||||||
@ -62,10 +58,6 @@ fun checkIsSupportedDeviceOrFinish(activity: Activity, tag: String): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun ArFragment.invoke(λ: ArFragment.() -> Unit) = λ()
|
|
||||||
|
|
||||||
fun isTrackig(trackable: Trackable) = trackable.trackingState == TrackingState.TRACKING
|
|
||||||
|
|
||||||
fun buildRenderable(
|
fun buildRenderable(
|
||||||
context: Context,
|
context: Context,
|
||||||
model: Uri,
|
model: Uri,
|
||||||
@ -81,55 +73,6 @@ fun buildRenderable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildMaterial(
|
|
||||||
context: Context,
|
|
||||||
color: Color,
|
|
||||||
onSuccess: (material: Material) -> Unit
|
|
||||||
) {
|
|
||||||
MaterialFactory
|
|
||||||
.makeOpaqueWithColor(context, color)
|
|
||||||
.thenAccept(onSuccess)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeColorOfMaterial(
|
|
||||||
context: Context,
|
|
||||||
color: Color,
|
|
||||||
renderable: Renderable
|
|
||||||
) {
|
|
||||||
val newColor = buildMaterial(context, color) {
|
|
||||||
renderable.material = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
fun buildRenderable(
|
|
||||||
context: Context,
|
|
||||||
model: RenderableSource,
|
|
||||||
modelUri: Uri,
|
|
||||||
onSuccess: (renderable: Renderable) -> Unit
|
|
||||||
) {
|
|
||||||
ModelRenderable.builder()
|
|
||||||
.setRegistryId(modelUri)
|
|
||||||
.setSource(context, model)
|
|
||||||
.build()
|
|
||||||
.thenAccept(onSuccess)
|
|
||||||
.exceptionally {
|
|
||||||
Log.e("SCENEFORM", "unable to load model", it)
|
|
||||||
return@exceptionally null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fetchModel(
|
|
||||||
context: Context,
|
|
||||||
source: Uri
|
|
||||||
) : RenderableSource {
|
|
||||||
return RenderableSource.builder()
|
|
||||||
.setSource(context, source, RenderableSource.SourceType.GLTF2)
|
|
||||||
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fun addTransformableNodeToScene(arFragment: ArFragment, anchor: Anchor, renderable: Renderable): Node {
|
fun addTransformableNodeToScene(arFragment: ArFragment, anchor: Anchor, renderable: Renderable): Node {
|
||||||
val anchorNode = AnchorNode(anchor)
|
val anchorNode = AnchorNode(anchor)
|
||||||
@ -142,7 +85,7 @@ fun addTransformableNodeToScene(arFragment: ArFragment, anchor: Anchor, renderab
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class CloudAnchorState {
|
enum class CloudAnchorState {
|
||||||
LOCAL,
|
NONE,
|
||||||
HOSTING,
|
HOSTING,
|
||||||
HOSTED,
|
HOSTED,
|
||||||
RESOLVING,
|
RESOLVING,
|
||||||
|
Loading…
Reference in New Issue
Block a user