Open In App

How to Build a Simple Augmented Reality Android App?

Augmented Reality has crossed a long way from Sci-fi Stories to Scientific reality. With this speed of technical advancement, it’s probably not very far when we can also manipulate digital data in this real physical world as Tony Stark did in his lab. When we superimpose information like sound, text, image to our real-world and also can interact with it through a special medium, that is Augmented Reality. The world-famous “Pokemon GO” app is just another example of an Augmented Reality Application. Let’s make a very simple Augmented Reality App in Android Studio using JAVA. This app shows a custom made or downloaded 3d model using the phone camera. A sample GIF is given below to get an idea about what we are going to do in this article.

Terminologies

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio

Note: 

  • Select Java as the programming language.
  • Note the location where the app is getting saved because we need that path later.
  • Choose ‘Minimum SDK‘ as ‘API 24: Android 7.0(Nougat)

Step 2: Getting the 3D Model

Sceneform 1.16.0 supports only glTF files. glTF means GL Transmission Format. Now .glb files are a binary version of the GL Transmission Format. These types of 3d model files are used in VR, AR  because it supports motion and animation.

Step 3: Downloading and Setting up SceneForm 1.16.0

Well, for AR apps we need Sceneform SDK. SceneForm 1.15.0 is very famous but recently, there are some plugin errors I faced while getting the “Google Sceneform Tools (Beta)” plugin in the latest Android Studio 4.1. So here I am, using the Sceneform 1.16.0 SDK and setting it up manually.

// this will add sceneformsrc folder into your project

include ‘:sceneform’

project(‘:sceneform’).projectDir = new File(‘sceneformsrc/sceneform’)

 

// this will add sceneformux folder into your project

include ‘:sceneformux’

project(‘:sceneformux’).projectDir = new File(‘sceneformux/ux’)

api project(“:sceneformux”)

dependencies {
   …
   implementation ‘com.google.ar:core:1.32.0’
}

// to support java 8 in your project

compileOptions {

       sourceCompatibility JavaVersion.VERSION_1_8

       targetCompatibility JavaVersion.VERSION_1_8

}




<!--This permits the user to access Camera-->
<uses-permission android:name="android.permission.CAMERA" />
 
<!--This helps to check a specific feature in the phone's hardware,
    here it is OpenGlES version. Sceneform needs OpenGLES Version 3.0 or later-->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
 
<!--Indicates that this app requires Google Play Services for AR.
    Limits app visibility in the Google Play Store to ARCore supported devices-->
<uses-feature android:name="android.hardware.camera.ar" android:required="true"/>

 After that add this line before the “activity” block.




<!-- ARCore need to be installed, as the app does not include any non-AR features.
     For an "AR Optional" app, specify "optional" instead of "required".-->
<meta-data android:name="com.google.ar.core" android:value="required" />

 
 Below is the complete code for the AndroidManifest.xml file. 




<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wheic.arapp">
 
    <!--This helps to permit the user to access Camera-->
    <uses-permission android:name="android.permission.CAMERA" />
     
    <!--This helps to check a specific feature in the phone's hardware,
        here it is OpenGl ES version 3-->
    <uses-feature
        android:glEsVersion="0x00030000"
        android:required="true" />
 
    <!--Here it is checking for AR feature in phone camera-->
    <uses-feature
        android:name="android.hardware.camera.ar"
        android:required="true" />
     
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ARApp">
 
        <meta-data
            android:name="com.google.ar.core"
            android:value="required" />
 
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

 
Step 4: Error Correction

Now comes a little boring part. The downloaded folders sceneformsrc and sceneformux contains some java file, that imports the java classes from an older android.support. So, now if you build the project you will see a lot of errors because of that. What you can do now is to migrate your project to the new Androidx. Now, you can find a way to migrate your whole project to Androidx or you can change the imports manually one by one. I know this is boring, but good things come to those who wait, right?

Step 5: Working with the activity_main.xml file




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <!--This is the fragment that will be used as AR camera-->
    <fragment
        android:id="@+id/arCameraArea"
        android:name="com.google.ar.sceneform.ux.ArFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
</androidx.constraintlayout.widget.ConstraintLayout>

Step 6: Working with MainActivity.java file




// object of ArFragment Class
private ArFragment arCam;




public static boolean checkSystemSupport(Activity activity) {
 
    // checking whether the API version of the running Android >= 24
      // that means Android Nougat 7.0
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 
        String openGlVersion = ((ActivityManager) Objects.requireNonNull(activity.getSystemService(Context.ACTIVITY_SERVICE))).getDeviceConfigurationInfo().getGlEsVersion();
 
        // checking whether the OpenGL version >= 3.0
        if (Double.parseDouble(openGlVersion) >= 3.0) {
            return true;
        } else {
            Toast.makeText(activity, "App needs OpenGl Version 3.0 or later", Toast.LENGTH_SHORT).show();
            activity.finish();
            return false;
        }
    } else {
        Toast.makeText(activity, "App does not support required Build Version", Toast.LENGTH_SHORT).show();
        activity.finish();
        return false;
    }
}




// ArFragment is linked up with its respective id used in the activity_main.xml
arCam = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.arCameraArea);




arCam.setOnTapArPlaneListener((hitResult, plane, motionEvent) -> {
    
      clickNo++;
   
    // the 3d model comes to the scene only the first time we tap the screen
    if (clickNo == 1) {
 
        Anchor anchor = hitResult.createAnchor();
        ModelRenderable.builder()
                .setSource(this, R.raw.gfg_gold_text_stand_2)
                .setIsFilamentGltf(true)
                .build()
                .thenAccept(modelRenderable -> addModel(anchor, modelRenderable))
                .exceptionally(throwable -> {
                    AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setMessage("Something is not right" + throwable.getMessage()).show();
                    return null;
                });
    }
});




private void addModel(Anchor anchor, ModelRenderable modelRenderable) {
 
      // Creating a AnchorNode with a specific anchor
    AnchorNode anchorNode = new AnchorNode(anchor);
     
      // attaching the anchorNode with the ArFragment
    anchorNode.setParent(arCam.getArSceneView().getScene());
    TransformableNode transform = new TransformableNode(arCam.getTransformationSystem());
       
      // attaching the anchorNode with the TransformableNode
    transform.setParent(anchorNode);
     
      // attaching the 3d model with the TransformableNode that is
      // already attached with the node
    transform.setRenderable(modelRenderable);
    transform.select();
}

 
Here is the complete code of the MainActivity.java file. Comments are added inside the code to understand the code in more detail. 




import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.ar.core.Anchor;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.ux.ArFragment;
import com.google.ar.sceneform.ux.TransformableNode;
import java.util.Objects;
 
public class MainActivity extends AppCompatActivity {
 
    // object of ArFragment Class
    private ArFragment arCam;
 
    // helps to render the 3d model
    // only once when we tap the screen
    private int clickNo = 0;
 
    public static boolean checkSystemSupport(Activity activity) {
 
        // checking whether the API version of the running Android >= 24
        // that means Android Nougat 7.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            String openGlVersion = ((ActivityManager) Objects.requireNonNull(activity.getSystemService(Context.ACTIVITY_SERVICE))).getDeviceConfigurationInfo().getGlEsVersion();
 
            // checking whether the OpenGL version >= 3.0
            if (Double.parseDouble(openGlVersion) >= 3.0) {
                return true;
            } else {
                Toast.makeText(activity, "App needs OpenGl Version 3.0 or later", Toast.LENGTH_SHORT).show();
                activity.finish();
                return false;
            }
        } else {
            Toast.makeText(activity, "App does not support required Build Version", Toast.LENGTH_SHORT).show();
            activity.finish();
            return false;
        }
    }
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        if (checkSystemSupport(this)) {
 
            // ArFragment is linked up with its respective id used in the activity_main.xml
            arCam = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.arCameraArea);          
            arCam.setOnTapArPlaneListener((hitResult, plane, motionEvent) -> {
                clickNo++;
                // the 3d model comes to the scene only
                // when clickNo is one that means once
                if (clickNo == 1) {
                    Anchor anchor = hitResult.createAnchor();
                    ModelRenderable.builder()
                            .setSource(this, R.raw.gfg_gold_text_stand_2)
                            .setIsFilamentGltf(true)
                            .build()
                            .thenAccept(modelRenderable -> addModel(anchor, modelRenderable))
                            .exceptionally(throwable -> {
                                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                                builder.setMessage("Something is not right" + throwable.getMessage()).show();
                                return null;
                            });
                }
            });
        } else {
            return;
        }
    }
 
    private void addModel(Anchor anchor, ModelRenderable modelRenderable) {
 
        // Creating a AnchorNode with a specific anchor
        AnchorNode anchorNode = new AnchorNode(anchor);
 
        // attaching the anchorNode with the ArFragment
        anchorNode.setParent(arCam.getArSceneView().getScene());
 
        // attaching the anchorNode with the TransformableNode
        TransformableNode model = new TransformableNode(arCam.getTransformationSystem());
        model.setParent(anchorNode);
 
        // attaching the 3d model with the TransformableNode
        // that is already attached with the node
        model.setRenderable(modelRenderable);
        model.select();
    }
}

 
Output: Run on a Physical Device

Finally, we built a simple Augmented Reality app using Android Studio. You can check this project in this GitHub link.

 


Article Tags :