Öğretici: Azure uzamsal bağlayıcıları kullanarak yeni bir Android uygulaması oluşturmaya yönelik adım adım yönergeler

Bu öğreticide, Azure uzamsal bağlayıcılarla en yeni işlevleri tümleştiren yeni bir Android uygulamasının nasıl oluşturulacağı gösterilmektedir.

Önkoşullar

Bu öğreticiyi tamamlamak için şunlar sahip olduğunuzdan emin olun:

Başlarken

Android Studio başlatın. Android Studio hoş geldiniz penceresinde Yeni bir Android Studio projesi Başlat ' a tıklayın.

  1. dosya -> yeni Project seçin.
  2. yeni Project oluştur penceresinde, Telefon ve Tablet bölümünde, boş etkinlik' i seçin ve ileri' ye tıklayın.
  3. yeni Project-boş etkinlik penceresinde aşağıdaki değerleri değiştirin:
    • Adı, paket adını ve kaydetme konumunu istediğiniz değerlerle değiştirin
    • Dili ayarlaJava
    • En düşük API düzeyini ayarlaAPI 26: Android 8.0 (Oreo)
    • Diğer seçenekleri oldukları gibi bırakın
    • Finish (Son) düğmesine tıklayın.
  4. Bileşen yükleyicisi çalışmaya çalışacaktır. Bazı işlemeden sonra, Android Studio IDE 'yi açar.

Android Studio-yeni Project

Deneniyor

Yeni uygulamanızı test etmek için, geliştirici özellikli cihazınızı bir USB kablosuyla geliştirme makinenize bağlayın. Android Studio bağlı cihazınızı seçin ve ' uygulama ' simgesini Çalıştır simgesine tıklayın. Android Studio uygulamayı bağlı cihazınıza yükleyip başlatır. Şimdi "Merhaba Dünya!" görmeniz gerekir cihazınızda çalışan uygulamada gösterilir. Çalıştır -> ' uygulamasını durdur' a tıklayın. Android Studio çalıştırma

Arcore tümleştirme

Arcore , genişletmiş gerçeklik deneyimlerini oluşturmaya yönelik Google 'ın platformu olduğundan, cihazın taşırken kendi konumunu izlemesini sağlar ve gerçek dünyanın kendisini öğrendiğini oluşturur.

app\manifests\AndroidManifest.xmlAşağıdaki girişleri kök düğümüne dahil etmek için değiştirin <manifest> . Bu kod parçacığı birkaç şeyi yapar:

  • Bu, uygulamanızın cihaz kameranıza erişmesine izin verir.
  • Ayrıca, uygulamanızın yalnızca ARCore destekleyen cihazlara Google Play Store göründüğünden emin olur.
  • Zaten yüklü değilse, uygulamanız yüklendiğinde, daha önce yüklü değilse, Google Play Store.
<manifest ...>

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera.ar" />

    <application>
        ...
        <meta-data android:name="com.google.ar.core" android:value="required" />
        ...
    </application>

</manifest>

Gradle Scripts\build.gradle (Module: app)Aşağıdaki girişi içerecek şekilde değiştirin. Bu kod, uygulamanızın ARCore sürüm 1,25 ' i hedeflediğinden emin olur. Bu değişiklikten sonra, Gradle adresinden eşitleme isteyip istemediğinizi soran bir bildirim alabilirsiniz: Şimdi Eşitle' ye tıklayın.

dependencies {
    ...
    implementation 'com.google.ar:core:1.25.0'
    ...
}

Manzara formunu tümleştirme

Manzara , bir OpenGL öğrenmeden, genişletilmiş gerçeklik uygulamalarında gerçekçi 3B sahneler oluşturmayı basit hale getirir.

Gradle Scripts\build.gradle (Module: app)Aşağıdaki girişleri içerecek şekilde değiştirin. Bu kod, uygulamanızın Java 8 ' den dil yapılarını kullanmasına izin verir ve bu da Sceneform gerektirir. Ayrıca, uygulamanızın 1,15 sürümünü hedeflediğinden emin olun Sceneform . Bu değişiklikten sonra, Gradle adresinden eşitleme isteyip istemediğinizi soran bir bildirim alabilirsiniz: Şimdi Eşitle' ye tıklayın.

android {
    ...

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    ...
    implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.15.0'
    ...
}

Dosyanızı açın app\res\layout\activity_main.xml ve var olan Hello Wolrd <TextView ... /> öğesini aşağıdaki arfragment ile değiştirin. Bu kod, kamera beslemenin ekranda görüntülenmesine neden olur, çünkü taşırken cihaz konumunuzu izlemek için ARCore 'u etkinleştirir.

<fragment android:name="com.google.ar.sceneform.ux.ArFragment"
    android:id="@+id/ux_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Not

Ana etkinliğinizin ham xml 'sini görmek için Android Studio sağ üst köşesindeki "Code" veya "Split" düğmesine tıklayın.

Uygulamanızı daha sonra doğrulamak için cihazınıza yeniden dağıtın . Bu kez, kamera izinlerine sorulur. Onaylandığında, kameranızın akış işlemesini ekranda görmeniz gerekir.

Gerçek dünyaya bir nesne yerleştirme

Uygulamanızı kullanarak bir nesne oluşturalım &. İlk olarak, aşağıdaki içeri aktarmaları içine ekleyin app\java\<PackageName>\MainActivity :

package com.example.myfirstapp;

import android.support.v7.app.AppCompatActivity;
import com.google.ar.core.Plane;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.math.Vector3;
import com.google.ar.sceneform.rendering.Color;
import com.google.ar.sceneform.rendering.MaterialFactory;
import com.google.ar.sceneform.rendering.Renderable;
import com.google.ar.sceneform.rendering.ShapeFactory;

`MainActivity`Tüm farklı öğeler birlikte yerleştirildikten sonra, tüm sınıf dosyaları nasıl görünmelidir. Bunu kendi dosyanıza göre karşılaştırmak için bir başvuru olarak kullanabilir ve herhangi bir farklılık varsa, herhangi bir farklılık olabilir.

Ardından, aşağıdaki üye değişkenlerini MainActivity sınıfınıza ekleyin:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.microsoft.azure.spatialanchors.AnchorLocateCriteria;
import com.microsoft.azure.spatialanchors.LocateAnchorStatus;

Ardından, aşağıdaki kodu app\java\<PackageName>\MainActivity onCreate() yöntemine ekleyin. Bu kod handleTap() , kullanıcının cihazınızdaki ekrana dokunduğunda tespit edilecek bir dinleyici () çağırılır. Tap 'ın izleme tarafından zaten tanınmış olan gerçek bir dünya yüzeyinde olması durumunda dinleyici çalışır.

private CloudSpatialAnchorSession cloudSession;

private String anchorId = null;
private boolean scanningForUpload = false;
private final Object syncSessionProgress = new Object();
private ExecutorService executorService = Executors.newSingleThreadExecutor();

    Scene scene = sceneView.getScene();

Son olarak, handleTap() her şeyi bir araya bağlayan aşağıdaki yöntemi ekleyin. Bir Sphere oluşturacak ve bunu, dokunduğunuz konuma yerleştirmeyecektir. Şimdi sıfır olarak ayarlandığından Sphere başlangıçta siyah olur this.recommendedSessionProgress . Bu değer, daha sonra ayarlanacak.


    this.tapExecuted = true;
}

if (this.anchorId != null) {
    this.anchorNode.getAnchor().detach();
    this.anchorNode.setParent(null);
    this.anchorNode = null;
    initializeSession();
MaterialFactory.makeOpaqueWithColor(this, new Color(
        this.recommendedSessionProgress,
    .thenAccept(material -> {
        this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), material);
        this.anchorNode.setRenderable(nodeRenderable);
        this.anchorNode.setParent(arFragment.getArSceneView().getScene());

        uploadCloudAnchorAsync(cloudAnchor)
            .thenAccept(id -> {
                this.anchorId = id;
                Log.i("ASAInfo", String.format("Cloud Anchor created: %s", this.anchorId));
                runOnUiThread(() -> {
    this.scanningForUpload = true;

Uygulamanızı daha sonra doğrulamak için cihazınıza yeniden dağıtın . Bu kez, ortamınızı tanımayı başlatmaya başlayabilmeniz için cihazınızın etrafında gezinebilirsiniz. Daha sonra ekrana dokunarak siyah küetinizi istediğiniz yüzey üzerine yerleştirin & oluşturun.

Yerel bir Azure uzamsal Bağlayıcısı ekleyin

Gradle Scripts\build.gradle (Module: app)Aşağıdaki girişi içerecek şekilde değiştirin. Bu örnek kod parçacığı, Azure uzamsal bağlayıcı SDK 'Sı 2.10.2 sürümünü hedefler. SDK sürümü 2.7.0 şu anda desteklenen en düşük sürüm olduğunu ve Azure uzamsal Tutturucuların daha yeni sürümlerine başvurulması gerektiğini unutmayın. Azure uzamsal bağlayıcı SDK 'sının en son sürümünü kullanmanızı öneririz. SDK sürüm notlarını buradan bulabilirsiniz.

dependencies {
    ...
    implementation 'com.microsoft.azure.spatialanchors:spatialanchors_jni:[2.10.2]'
    implementation 'com.microsoft.azure.spatialanchors:spatialanchors_java:[2.10.2]'
    ...
}

Azure uzamsal çıpası SDK 2.10.0 veya üzerini hedefliyorsanız, projenizin dosyasının depolar bölümüne aşağıdaki girişi ekleyin settings.gradle . Bu, SDK 2.10.0 veya üzeri için Azure uzamsal bağlayıcı Android paketlerini barındıran Maven paketi akışına yönelik URL 'YI içerir:

dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven {
            url 'https://pkgs.dev.azure.com/aipmr/MixedReality-Unity-Packages/_packaging/Maven-packages/maven/v1'
        }
        ...
    }
}

app\java\<PackageName> -> Yeni -> Java sınıfı' na sağ tıklayın. Adı MyFirstApp olarak ayarlayın ve sınıf' ı seçin. Çağrılan bir dosya MyFirstApp.java oluşturulacaktır. Aşağıdaki içeri aktarmayı ekleyin:

import com.microsoft.CloudServices;

android.app.ApplicationÜst sınıfı olarak tanımlayın.

public class MyFirstApp extends android.app.Application {...

Ardından, yeni sınıfın içine aşağıdaki kodu ekleyin MyFirstApp . Bu, Azure uzamsal bağlayıcıların uygulamanızın bağlamıyla başlatılmış olmasını sağlayacaktır.

    @Override
    public void onCreate() {
        super.onCreate();
        CloudServices.initialize(this);
    }

Şimdi, app\manifests\AndroidManifest.xml kök düğümüne aşağıdaki girdiyi eklemek için değiştirin <application> . Bu kod, uygulamanızda oluşturduğunuz uygulama sınıfını yedekler.

    <application
        android:name=".MyFirstApp"
        ...
    </application>

Uygulamasına geri dönerek app\java\<PackageName>\MainActivity aşağıdaki içeri aktarmaları ekleyin:


## <a name="putting-everything-together"></a>Her şeyi birlikte yerleştirme
`MainActivity`Tüm farklı öğeler birlikte yerleştirildikten sonra, tüm sınıf dosyaları nasıl görünmelidir. Bunu kendi dosyanıza göre karşılaştırmak için bir başvuru olarak kullanabilir ve herhangi bir farklılık varsa, herhangi bir farklılık olabilir.
import android.os.Bundle;
import com.google.ar.core.HitResult;

import android.view.MotionEvent;

Ardından, aşağıdaki üye değişkenlerini MainActivity sınıfınıza ekleyin:


public class MainActivity extends AppCompatActivity {

    private boolean tapExecuted = false;
    private final Object syncTaps = new Object();

Daha sonra, aşağıdaki initializeSession() yöntemi sınıfınızın içine ekleyelim mainActivity . Bir kez çağrıldıktan sonra, uygulamanızın başlatılması sırasında bir Azure uzamsal bağlayıcı oturumunun oluşturulduğundan ve düzgün başlatılmış olduğundan emin olur. Bu kod, bir erken dönerek, çağrı aracılığıyla ASA oturumuna geçirilen manzara oturumunun null olmadığından emin olur cloudSession.setSession .

private void initializeSession() {
    if (this.cloudSession != null){
        this.cloudSession.close();
    }
    this.cloudSession = new CloudSpatialAnchorSession();
    this.cloudSession.setSession(sceneView.getSession());
    this.cloudSession.setLogLevel(SessionLogLevel.Information);
    this.cloudSession.addOnLogDebugListener(args -> Log.d("ASAInfo", args.getMessage()));
    this.cloudSession.addErrorListener(args -> Log.e("ASAError", String.format("%s: %s", args.getErrorCode().name(), args.getErrorMessage())));

    this.cloudSession.addSessionUpdatedListener(args -> {
        synchronized (this.syncSessionProgress) {
            this.recommendedSessionProgress = args.getStatus().getRecommendedForCreateProgress();
            Log.i("ASAInfo", String.format("Session progress: %f", this.recommendedSessionProgress));
            if (!this.scanningForUpload)
            {
    synchronized (this.syncTaps) {

, initializeSession() Bir erken geri dönüş yapabildiği için, manzara oturumu henüz kurulu değilse (örneğin, sceneView.getSession() null ise), bir OnUpdate çağrısı ekler.

Şimdi initializeSession() ve scene_OnUpdate(...) yönteminizi yönteminizin içine verlim onCreate() . Ayrıca, kamera akışınızdan gelen çerçevelerin işlenmek üzere Azure uzamsal bağlayıcı SDK 'sına gönderilmesini de sağlamaktır.

private CloudSpatialAnchorSession cloudSession;

private String anchorId = null;
private boolean scanningForUpload = false;
private final Object syncSessionProgress = new Object();
private ExecutorService executorService = Executors.newSingleThreadExecutor();

// <onCreate>
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    this.arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
    this.arFragment.setOnTapArPlaneListener(this::handleTap);

    this.sceneView = arFragment.getArSceneView();
    Scene scene = sceneView.getScene();

Son olarak, aşağıdaki kodu handleTap() yöntemine ekleyin. Gerçek dünyaya yerleştirdiğimiz siyah sphere öğesine yerel bir Azure uzamsal Bağlayıcısı ekler.


    this.tapExecuted = true;
}

if (this.anchorId != null) {
    this.anchorNode.getAnchor().detach();
    this.anchorNode.setParent(null);
    this.anchorNode = null;
    initializeSession();
MaterialFactory.makeOpaqueWithColor(this, new Color(
        this.recommendedSessionProgress,
        this.recommendedSessionProgress,
        this.recommendedSessionProgress))
    .thenAccept(material -> {
        this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), material);
        this.anchorNode.setRenderable(nodeRenderable);
        this.anchorNode.setParent(arFragment.getArSceneView().getScene());

        uploadCloudAnchorAsync(cloudAnchor)
            .thenAccept(id -> {
                this.anchorId = id;
                Log.i("ASAInfo", String.format("Cloud Anchor created: %s", this.anchorId));
                runOnUiThread(() -> {
    this.scanningForUpload = true;

Uygulamanızı bir kez daha yeniden dağıtın . Cihazınızın etrafında ilerleyin, ekrana dokunun ve bir siyah küre yerleştirin. Bu süre, ancak kodunuz, kürenin yerel bir Azure uzamsal bağlayıcısını oluşturur ve iliştirir.

Daha sonra devam etmeden önce, hesap tanımlayıcı, anahtar ve etki alanını daha önce yoksa, bir Azure uzamsal bağlayıcı hesabı oluşturmanız gerekir. Elde etmek için aşağıdaki bölümü izleyin.

Uzamsal bağlayıcı kaynağı oluşturma

Azure Portal gidin.

Sol bölmede kaynak oluştur' u seçin.

Uzamsal bağlantıları aramak için arama kutusunu kullanın.

Uzamsal Tutturucuların aramasının sonuçlarını gösteren ekran görüntüsü.

Uzamsal bağlayıcıları seçin ve ardından Oluştur' u seçin.

Uzamsal bağlayıcı hesabı bölmesinde şunları yapın:

  • Normal alfasayısal karakterleri kullanarak benzersiz bir kaynak adı girin.

  • Kaynağı iliştirmek istediğiniz aboneliği seçin.

  • Yeni oluştur seçeneğini belirleyerek bir kaynak grubu oluşturun. Myresourcegroup olarak adlandırın ve ardından Tamam' ı seçin.

    Kaynak grubu , Web uygulamaları, veritabanları ve depolama hesapları gibi Azure kaynaklarının dağıtıldığı ve yönetildiği bir mantıksal kapsayıcıdır. Örneğin, daha sonra tek bir basit adımda kaynak grubun tamamını silmeyi seçebilirsiniz.

  • Kaynağın yerleştirileceği bir konum (bölge) seçin.

  • Kaynağı oluşturmaya başlamak için Oluştur ' u seçin.

Kaynak oluşturmak için uzamsal bağlayıcı bölmesinin ekran görüntüsü.

Kaynak oluşturulduktan sonra, Azure portal dağıtımınızın tamamlandığını gösterir.

Kaynak dağıtımının tamamlandığını gösteren ekran görüntüsü.

Kaynağa git’i seçin. Artık kaynak özelliklerini görüntüleyebilirsiniz.

Kaynağın hesap kimliği değerini daha sonra kullanmak üzere bir metin düzenleyicisine kopyalayın.

Kaynak özellikleri bölmesinin ekran görüntüsü.

Ayrıca, kaynağın hesap etki alanı değerini daha sonra kullanmak üzere bir metin düzenleyicisine kopyalayın.

Kaynağın hesap etki alanı değerini gösteren ekran görüntüsü.

Ayarlar altında erişim anahtarı' nı seçin. Birincil anahtar değerini, hesap anahtarını daha sonra kullanmak üzere bir metin düzenleyicisine kopyalayın.

Hesap için anahtarlar bölmesinin ekran görüntüsü.

yerel bağlantı yayınınızı buluta Upload

Azure uzamsal bağlayıcılarınızın hesap tanımlayıcısı, anahtarınız ve etki alanınız olduktan sonra geri dönüp app\java\<PackageName>\MainActivity içine aşağıdaki içeri aktarmaları ekleyebilirsiniz:


import com.google.ar.sceneform.ArSceneView;
import com.google.ar.sceneform.Scene;
import com.microsoft.azure.spatialanchors.CloudSpatialAnchor;
import com.microsoft.azure.spatialanchors.CloudSpatialAnchorSession;
import com.microsoft.azure.spatialanchors.SessionLogLevel;

Ardından, aşağıdaki üye değişkenlerini MainActivity sınıfınıza ekleyin:

private final Object syncTaps = new Object();
private ArFragment arFragment;
private AnchorNode anchorNode;
private Renderable nodeRenderable = null;
private float recommendedSessionProgress = 0f;

Şimdi aşağıdaki kodu yönteminize initializeSession() ekleyin. İlk olarak bu kod, uygulamanın kamera akışından kare toplayan Azure Spatial Anchors SDK'sı tarafından yapılan ilerleme durumunu izlemesine olanak sağlar. Bu şekilde, kürenizin rengi özgün siyah renginden griye değişmeye başlar. Ardından, yer yer yer imtiyazını buluta göndermek için yeterli kare toplanmışsa beyaza döner. İkincisi, bu kod bulut arka uç ile iletişim kurmak için gereken kimlik bilgilerini sağlar. Burada, uygulamalarınızı hesap Tanımlayıcınızı, Anahtarınızı ve Etki Alanınızı kullanmak üzere yapılandırabilirsiniz. Kaynak için kaynak ayarlarken bunları bir metin düzenleyicisine Spatial Anchors.

private void initializeSession() {
    if (this.cloudSession != null){
        this.cloudSession.close();
    }
    this.cloudSession = new CloudSpatialAnchorSession();
    this.cloudSession.setSession(sceneView.getSession());
    this.cloudSession.setLogLevel(SessionLogLevel.Information);
    this.cloudSession.addOnLogDebugListener(args -> Log.d("ASAInfo", args.getMessage()));
    this.cloudSession.addErrorListener(args -> Log.e("ASAError", String.format("%s: %s", args.getErrorCode().name(), args.getErrorMessage())));

    this.cloudSession.addSessionUpdatedListener(args -> {
        synchronized (this.syncSessionProgress) {
            this.recommendedSessionProgress = args.getStatus().getRecommendedForCreateProgress();
            Log.i("ASAInfo", String.format("Session progress: %f", this.recommendedSessionProgress));
            if (!this.scanningForUpload)
            {
                return;
            }
        }

        runOnUiThread(() -> {
            synchronized (this.syncSessionProgress) {
                MaterialFactory.makeOpaqueWithColor(this, new Color(
                        this.recommendedSessionProgress,
                        this.recommendedSessionProgress,
                        this.recommendedSessionProgress))
                    .thenAccept(material -> {
                        this.nodeRenderable.setMaterial(material);
                    });
            }
        });
    });

    this.cloudSession.addAnchorLocatedListener(args -> {
        if (args.getStatus() == LocateAnchorStatus.Located)
        {
            runOnUiThread(()->{
                this.anchorNode = new AnchorNode();
                this.anchorNode.setAnchor(args.getAnchor().getLocalAnchor());
// </initializeSession>

// <handleTap>
protected void handleTap(HitResult hitResult, Plane plane, MotionEvent motionEvent) {
    synchronized (this.syncTaps) {

Ardından sınıfınıza uploadCloudAnchorAsync() aşağıdaki yöntemi mainActivity ekleyin. Çağrıldıktan sonra, bu yöntem cihazdan yeterli kare toplanana kadar zaman uyumsuz olarak bekler. Bu gerçekleşir başlamaz, kürenizin rengini sarıya değiştirir ve ardından yerel Azure Spatial Anchor'nizi buluta yüklemeye başlar. Karşıya yükleme tamam olduktan sonra kod bir sabit noktası tanımlayıcısı geri döner.

private CompletableFuture<String> uploadCloudAnchorAsync(CloudSpatialAnchor anchor) {
    synchronized (this.syncSessionProgress) {
        this.scanningForUpload = true;
    }

    return CompletableFuture.runAsync(() -> {
        try {
            float currentSessionProgress;
            do {
                synchronized (this.syncSessionProgress) {
                    currentSessionProgress = this.recommendedSessionProgress;
                }
                if (currentSessionProgress < 1.0) {
                    Thread.sleep(500);
                }
            }
            while (currentSessionProgress < 1.0);

            synchronized (this.syncSessionProgress) {
                this.scanningForUpload = false;
            }
            runOnUiThread(() -> {
                MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.YELLOW))
                    .thenAccept(yellowMaterial -> {
                        this.nodeRenderable.setMaterial(yellowMaterial);
                    });
            });

            this.cloudSession.createAnchorAsync(anchor).get();
        } catch (InterruptedException | ExecutionException e) {
            Log.e("ASAError", e.toString());
            throw new RuntimeException(e);
        }
    }, executorService).thenApply(ignore -> anchor.getIdentifier());
}

Son olarak, her şeyi birlikte bağlayacağız. yönteminize handleTap() aşağıdaki kodu ekleyin. Bu işlem, uploadCloudAnchorAsync() küreniz oluşturulur oluşturulmaz yönteminizi çağırır. Yöntem geri döndüğünde aşağıdaki kod, kürenizin rengini mavi olarak değiştirerek son bir güncelleştirme gerçekleştirecek.


        this.tapExecuted = true;
    }

    if (this.anchorId != null) {
        this.anchorNode.getAnchor().detach();
        this.anchorNode.setParent(null);
        this.anchorNode = null;
        initializeSession();
    MaterialFactory.makeOpaqueWithColor(this, new Color(
            this.recommendedSessionProgress,
            this.recommendedSessionProgress,
            this.recommendedSessionProgress))
        .thenAccept(material -> {
            this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), material);
            this.anchorNode.setRenderable(nodeRenderable);
            this.anchorNode.setParent(arFragment.getArSceneView().getScene());

            uploadCloudAnchorAsync(cloudAnchor)
                .thenAccept(id -> {
                    this.anchorId = id;
                    Log.i("ASAInfo", String.format("Cloud Anchor created: %s", this.anchorId));
                    runOnUiThread(() -> {
                        MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.BLUE))
                            .thenAccept(blueMaterial -> {
                                this.nodeRenderable.setMaterial(blueMaterial);
                                synchronized (this.syncTaps) {
                                    this.tapExecuted = false;
                                }
                            });
                    });
                });
        });
}
// </handleTap>

// <uploadCloudAnchorAsync>
private CompletableFuture<String> uploadCloudAnchorAsync(CloudSpatialAnchor anchor) {
    synchronized (this.syncSessionProgress) {
        this.scanningForUpload = true;

Uygulamalarınızı bir kez daha yeniden kullanın. Cihazınızın etrafında hareket edin, ekrana dokunun ve sphere'inizi yer. Ancak bu kez, kamera kareleri toplanmış olarak, kürenizin rengi siyahtan beyaza değişecektir. Yeterli karemiz olduktan sonra, küre sarıya döner ve buluta yükleme başlar. Telefonunuzun İnternet'e bağlı olduğundan emin olun. Karşıya yükleme tamam olduktan sonra küreniz maviye döner. İsteğe bağlı olarak, Logcat uygulamanın Android Studio günlük iletilerini görüntülemek için pencerede pencereyi izleyebilirsiniz. Günlüğe kaydedecek iletilere örnek olarak, kare yakalama sırasında oturum ilerleme durumu ve karşıya yükleme tamamlandıktan sonra bulutun döndüreceği yer sabit noktası tanımlayıcısı dahildir.

Not

değerini görmüyorsanız (hata ayıklama günlüklerinde olarak adlandırılır) değişiklik olursa, telefonunuzu yerleştirilen kürenin etrafında hem hareket ettirin hem de döndürerek emin recommendedSessionProgress Session progress olun.

Bulut uzamsal sabit noktasını bulun

Yer bağlantınız buluta yüklendiktan sonra yeniden bulma girişiminde hazırız. İlk olarak, kodunuza aşağıdaki içeri aktarmaları ekleriz.

import com.microsoft.azure.spatialanchors.SessionLogLevel;

import com.google.ar.sceneform.ux.ArFragment;
import android.util.Log;

Ardından, aşağıdaki kodu yönteminize handleTap() ekleriz. Bu kod şunları sağlar:

  • Mevcut mavi küremizi ekrandan kaldırın.
  • Azure Spatial Anchors yeniden başlatabilirsiniz. Bu eylem, belirleyeceğimiz yer noktasının oluşturduğu yerel yer noktası yerine buluttan geldiğinden emin olur.
  • Buluta yüklediğiniz yer noktası için bir sorgu oluşturun.
protected void handleTap(HitResult hitResult, Plane plane, MotionEvent motionEvent) {
    synchronized (this.syncTaps) {
        if (this.tapExecuted) {
            return;
        }

        this.tapExecuted = true;
    }

    if (this.anchorId != null) {
        this.anchorNode.getAnchor().detach();
        this.anchorNode.setParent(null);
        this.anchorNode = null;
        initializeSession();
        AnchorLocateCriteria criteria = new AnchorLocateCriteria();
        criteria.setIdentifiers(new String[]{this.anchorId});
        cloudSession.createWatcher(criteria);
        return;
    }

    this.anchorNode = new AnchorNode();
    this.anchorNode.setAnchor(hitResult.createAnchor());
    CloudSpatialAnchor cloudAnchor = new CloudSpatialAnchor();
    cloudAnchor.setLocalAnchor(this.anchorNode.getAnchor());

    MaterialFactory.makeOpaqueWithColor(this, new Color(
            this.recommendedSessionProgress,
            this.recommendedSessionProgress,
            this.recommendedSessionProgress))
        .thenAccept(material -> {
            this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), material);
            this.anchorNode.setRenderable(nodeRenderable);
            this.anchorNode.setParent(arFragment.getArSceneView().getScene());

            uploadCloudAnchorAsync(cloudAnchor)
                .thenAccept(id -> {
                    this.anchorId = id;
                    Log.i("ASAInfo", String.format("Cloud Anchor created: %s", this.anchorId));
                    runOnUiThread(() -> {
                        MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.BLUE))
                            .thenAccept(blueMaterial -> {
                                this.nodeRenderable.setMaterial(blueMaterial);
                                synchronized (this.syncTaps) {
                                    this.tapExecuted = false;
                                }
                            });
                    });
                });
        });
}

Şimdi, sorgulaycamız yer noktası bulunduğu zaman çağrılan kodu bağlayabilirsiniz. yönteminizin initializeSession() içine aşağıdaki kodu ekleyin. Bu kod parçacığı, & uzamsal sabit noktası bulunduktan sonra yeşil bir küreye yerleştirilebilir. Ayrıca ekran dokunmayı yeniden etkinleştirerek senaryonun tamamını tekrarlayın: başka bir yerel yer bağlantısı oluşturun, karşıya yükleyin ve yeniden bulun.

private void initializeSession() {
    if (this.cloudSession != null){
        this.cloudSession.close();
    }
    this.cloudSession = new CloudSpatialAnchorSession();
    this.cloudSession.setSession(sceneView.getSession());
    this.cloudSession.setLogLevel(SessionLogLevel.Information);
    this.cloudSession.addOnLogDebugListener(args -> Log.d("ASAInfo", args.getMessage()));
    this.cloudSession.addErrorListener(args -> Log.e("ASAError", String.format("%s: %s", args.getErrorCode().name(), args.getErrorMessage())));

    this.cloudSession.addSessionUpdatedListener(args -> {
        synchronized (this.syncSessionProgress) {
            this.recommendedSessionProgress = args.getStatus().getRecommendedForCreateProgress();
            Log.i("ASAInfo", String.format("Session progress: %f", this.recommendedSessionProgress));
            if (!this.scanningForUpload)
            {
                return;
            }
        }

        runOnUiThread(() -> {
            synchronized (this.syncSessionProgress) {
                MaterialFactory.makeOpaqueWithColor(this, new Color(
                        this.recommendedSessionProgress,
                        this.recommendedSessionProgress,
                        this.recommendedSessionProgress))
                    .thenAccept(material -> {
                        this.nodeRenderable.setMaterial(material);
                    });
            }
        });
    });

    this.cloudSession.addAnchorLocatedListener(args -> {
        if (args.getStatus() == LocateAnchorStatus.Located)
        {
            runOnUiThread(()->{
                this.anchorNode = new AnchorNode();
                this.anchorNode.setAnchor(args.getAnchor().getLocalAnchor());
                MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.GREEN))
                    .thenAccept(greenMaterial -> {
                        this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), greenMaterial);
                        this.anchorNode.setRenderable(nodeRenderable);
                        this.anchorNode.setParent(arFragment.getArSceneView().getScene());

                        this.anchorId = null;
                        synchronized (this.syncTaps) {
                            this.tapExecuted = false;
                        }
                    });
            });
        }
    });

    this.cloudSession.getConfiguration().setAccountId(/* Copy your account Identifier in here */);
    this.cloudSession.getConfiguration().setAccountKey(/* Copy your account Key in here */);
    this.cloudSession.getConfiguration().setAccountDomain(/* Copy your account Domain in here */);
    this.cloudSession.start();
}

İşte bu kadar! Senaryonun tamamını sona kadar denemek için, uygulamayı son bir kez yeniden kullanın. Cihazınızın etrafında hareket edin ve siyah kürenizi yer. Ardından, küre sarıya dönüşene kadar kamera çerçevelerini yakalamak için cihazınızı taşımaya devam edin. Yerel yer bağlantınız karşıya yük binecek ve küreniz maviye döner. Son olarak, ekranınıza bir kez daha dokunarak yerel yer yer bağlantınız kaldırıldıktan sonra bunun buluta uygun olduğunu sorgulayız. Bulut uzamsal sabit noktası bulunana kadar cihazınızı taşımaya devam edin. Yeşil bir kürenin doğru konumda görünmesi gerekir ve senaryonun tamamını & tekrarlamanız gerekir.

Her şeyi bir araya getirdi

Tüm farklı öğeler bir MainActivity araya geldikten sonra tam sınıf dosyasının nasıl olması gerektiği burada açıklanmıştır. Bunu kendi dosyanıza göre karşılaştırmak için bir başvuru olarak kullanabilir ve herhangi bir fark kaldı mı?

Her şeyi birlikte yerleştirme

MainActivityTüm farklı öğeler birlikte yerleştirildikten sonra, tüm sınıf dosyaları nasıl görünmelidir. Bunu kendi dosyanıza göre karşılaştırmak için bir başvuru olarak kullanabilir ve herhangi bir farklılık varsa, herhangi bir farklılık olabilir.

package com.example.myfirstapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.google.ar.core.HitResult;
import com.google.ar.core.Plane;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.math.Vector3;
import com.google.ar.sceneform.rendering.Color;
import com.google.ar.sceneform.rendering.MaterialFactory;
import com.google.ar.sceneform.rendering.Renderable;
import com.google.ar.sceneform.rendering.ShapeFactory;
import com.google.ar.sceneform.ux.ArFragment;

import android.view.MotionEvent;
import android.util.Log;

import com.google.ar.sceneform.ArSceneView;
import com.google.ar.sceneform.Scene;
import com.microsoft.azure.spatialanchors.CloudSpatialAnchor;
import com.microsoft.azure.spatialanchors.CloudSpatialAnchorSession;
import com.microsoft.azure.spatialanchors.SessionLogLevel;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.microsoft.azure.spatialanchors.AnchorLocateCriteria;
import com.microsoft.azure.spatialanchors.LocateAnchorStatus;

public class MainActivity extends AppCompatActivity {

    private boolean tapExecuted = false;
    private final Object syncTaps = new Object();
    private ArFragment arFragment;
    private AnchorNode anchorNode;
    private Renderable nodeRenderable = null;
    private float recommendedSessionProgress = 0f;

    private ArSceneView sceneView;
    private CloudSpatialAnchorSession cloudSession;

    private String anchorId = null;
    private boolean scanningForUpload = false;
    private final Object syncSessionProgress = new Object();
    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    // <onCreate>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
        this.arFragment.setOnTapArPlaneListener(this::handleTap);

        this.sceneView = arFragment.getArSceneView();
        Scene scene = sceneView.getScene();
        scene.addOnUpdateListener(frameTime -> {
            if (this.cloudSession != null) {
                this.cloudSession.processFrame(sceneView.getArFrame());
            }
        });

        initializeSession();
    }
    // </onCreate>

    // <initializeSession>
    private void initializeSession() {
        if (this.cloudSession != null){
            this.cloudSession.close();
        }
        this.cloudSession = new CloudSpatialAnchorSession();
        this.cloudSession.setSession(sceneView.getSession());
        this.cloudSession.setLogLevel(SessionLogLevel.Information);
        this.cloudSession.addOnLogDebugListener(args -> Log.d("ASAInfo", args.getMessage()));
        this.cloudSession.addErrorListener(args -> Log.e("ASAError", String.format("%s: %s", args.getErrorCode().name(), args.getErrorMessage())));

        this.cloudSession.addSessionUpdatedListener(args -> {
            synchronized (this.syncSessionProgress) {
                this.recommendedSessionProgress = args.getStatus().getRecommendedForCreateProgress();
                Log.i("ASAInfo", String.format("Session progress: %f", this.recommendedSessionProgress));
                if (!this.scanningForUpload)
                {
                    return;
                }
            }

            runOnUiThread(() -> {
                synchronized (this.syncSessionProgress) {
                    MaterialFactory.makeOpaqueWithColor(this, new Color(
                            this.recommendedSessionProgress,
                            this.recommendedSessionProgress,
                            this.recommendedSessionProgress))
                        .thenAccept(material -> {
                            this.nodeRenderable.setMaterial(material);
                        });
                }
            });
        });

        this.cloudSession.addAnchorLocatedListener(args -> {
            if (args.getStatus() == LocateAnchorStatus.Located)
            {
                runOnUiThread(()->{
                    this.anchorNode = new AnchorNode();
                    this.anchorNode.setAnchor(args.getAnchor().getLocalAnchor());
                    MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.GREEN))
                        .thenAccept(greenMaterial -> {
                            this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), greenMaterial);
                            this.anchorNode.setRenderable(nodeRenderable);
                            this.anchorNode.setParent(arFragment.getArSceneView().getScene());

                            this.anchorId = null;
                            synchronized (this.syncTaps) {
                                this.tapExecuted = false;
                            }
                        });
                });
            }
        });

        this.cloudSession.getConfiguration().setAccountId(/* Copy your account Identifier in here */);
        this.cloudSession.getConfiguration().setAccountKey(/* Copy your account Key in here */);
        this.cloudSession.getConfiguration().setAccountDomain(/* Copy your account Domain in here */);
        this.cloudSession.start();
    }
    // </initializeSession>

    // <handleTap>
    protected void handleTap(HitResult hitResult, Plane plane, MotionEvent motionEvent) {
        synchronized (this.syncTaps) {
            if (this.tapExecuted) {
                return;
            }

            this.tapExecuted = true;
        }

        if (this.anchorId != null) {
            this.anchorNode.getAnchor().detach();
            this.anchorNode.setParent(null);
            this.anchorNode = null;
            initializeSession();
            AnchorLocateCriteria criteria = new AnchorLocateCriteria();
            criteria.setIdentifiers(new String[]{this.anchorId});
            cloudSession.createWatcher(criteria);
            return;
        }

        this.anchorNode = new AnchorNode();
        this.anchorNode.setAnchor(hitResult.createAnchor());
        CloudSpatialAnchor cloudAnchor = new CloudSpatialAnchor();
        cloudAnchor.setLocalAnchor(this.anchorNode.getAnchor());

        MaterialFactory.makeOpaqueWithColor(this, new Color(
                this.recommendedSessionProgress,
                this.recommendedSessionProgress,
                this.recommendedSessionProgress))
            .thenAccept(material -> {
                this.nodeRenderable = ShapeFactory.makeSphere(0.1f, new Vector3(0.0f, 0.15f, 0.0f), material);
                this.anchorNode.setRenderable(nodeRenderable);
                this.anchorNode.setParent(arFragment.getArSceneView().getScene());

                uploadCloudAnchorAsync(cloudAnchor)
                    .thenAccept(id -> {
                        this.anchorId = id;
                        Log.i("ASAInfo", String.format("Cloud Anchor created: %s", this.anchorId));
                        runOnUiThread(() -> {
                            MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.BLUE))
                                .thenAccept(blueMaterial -> {
                                    this.nodeRenderable.setMaterial(blueMaterial);
                                    synchronized (this.syncTaps) {
                                        this.tapExecuted = false;
                                    }
                                });
                        });
                    });
            });
    }
    // </handleTap>

    // <uploadCloudAnchorAsync>
    private CompletableFuture<String> uploadCloudAnchorAsync(CloudSpatialAnchor anchor) {
        synchronized (this.syncSessionProgress) {
            this.scanningForUpload = true;
        }

        return CompletableFuture.runAsync(() -> {
            try {
                float currentSessionProgress;
                do {
                    synchronized (this.syncSessionProgress) {
                        currentSessionProgress = this.recommendedSessionProgress;
                    }
                    if (currentSessionProgress < 1.0) {
                        Thread.sleep(500);
                    }
                }
                while (currentSessionProgress < 1.0);

                synchronized (this.syncSessionProgress) {
                    this.scanningForUpload = false;
                }
                runOnUiThread(() -> {
                    MaterialFactory.makeOpaqueWithColor(this, new Color(android.graphics.Color.YELLOW))
                        .thenAccept(yellowMaterial -> {
                            this.nodeRenderable.setMaterial(yellowMaterial);
                        });
                });

                this.cloudSession.createAnchorAsync(anchor).get();
            } catch (InterruptedException | ExecutionException e) {
                Log.e("ASAError", e.toString());
                throw new RuntimeException(e);
            }
        }, executorService).thenApply(ignore -> anchor.getIdentifier());
    }
    // </uploadCloudAnchorAsync>
}

Sonraki adımlar

Bu öğreticide, Azure uzamsal bağlayıcılarla en önemli işlevselliği tümleştiren yeni bir Android uygulaması oluşturmayı öğrendiniz. Azure uzamsal bağlayıcı Kitaplığı hakkında daha fazla bilgi edinmek için, bağlantıları oluşturma ve bulma konusundaki kılavuzumuza devam edin.

Sonraki adımlar

Bu öğreticide ARCore işlevselliğini Azure Spatial Anchors ile tümleştiren yeni bir Android uygulaması oluşturma hakkında bilgi Spatial Anchors. Azure Spatial Anchors kitaplığı hakkında daha fazla bilgi edinmek için yer noktalarını oluşturma ve bulma kılavuzumuza devam edin.