Skip to content

Instantly share code, notes, and snippets.

@russell-brady
Created March 6, 2019 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save russell-brady/368ce2509a557d9701b040987096c39c to your computer and use it in GitHub Desktop.
Save russell-brady/368ce2509a557d9701b040987096c39c to your computer and use it in GitHub Desktop.
Ar Cloud Anchor Activity
package com.arproject.russell.ar_t.ar;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.widget.Button;
import android.widget.Toast;
import com.arproject.russell.ar_t.R;
import com.arproject.russell.ar_t.augmentedimages.helpers.SnackbarHelper;
import com.google.ar.core.Anchor;
import com.google.ar.core.HitResult;
import com.google.ar.core.Plane;
import com.google.ar.sceneform.AnchorNode;
import com.google.ar.sceneform.FrameTime;
import com.google.ar.sceneform.assets.RenderableSource;
import com.google.ar.sceneform.rendering.ModelRenderable;
import com.google.ar.sceneform.ux.ArFragment;
import com.google.ar.sceneform.ux.TransformableNode;
public class ArActivity extends AppCompatActivity {
private enum AppAnchorState {
NONE,
HOSTING,
HOSTED,
RESOLVING,
RESOLVED
}
private AppAnchorState appAnchorState = AppAnchorState.NONE;
private static final String TAG = ArActivity.class.getSimpleName();
private static final double MIN_OPENGL_VERSION = 3.0;
private static final String GLTF_ASSET = "https://raw.githubusercontent.com/yudiz-solutions/runtime_ar_android/master/model/model.gltf";
private ArFragment arFragment;
private ModelRenderable renderable;
private SnackbarHelper snackbarHelper;
private Anchor cloudAnchor;
@Override
@SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"})
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!checkIsSupportedDeviceOrFinish(this)) {
return;
}
snackbarHelper = new SnackbarHelper();
setContentView(R.layout.activity_ux);
arFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.sceneform_fragment);
arFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdateFrame);
ModelRenderable.builder()
.setSource(this, RenderableSource.builder().setSource(
this,
Uri.parse(GLTF_ASSET),
RenderableSource.SourceType.GLTF2)
.build())
.setRegistryId(GLTF_ASSET)
.build()
.thenAccept(renderable -> this.renderable = renderable)
.exceptionally(
throwable -> {
Toast toast =
Toast.makeText(this, "Unable to load renderable " +
GLTF_ASSET, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
return null;
});
Button clearButton = findViewById(R.id.clear_button);
clearButton.setOnClickListener(view -> setCloudAnchor(null));
Button hostButton = findViewById(R.id.host_button);
hostButton.setOnClickListener(view -> hostModel());
arFragment.setOnTapArPlaneListener(
(HitResult hitResult, Plane plane, MotionEvent motionEvent) -> {
if (renderable == null) {
return;
}
// Create the Anchor.
Anchor anchor = hitResult.createAnchor();
setCloudAnchor(anchor);
AnchorNode anchorNode = new AnchorNode(cloudAnchor);
TransformableNode node = new TransformableNode(arFragment.getTransformationSystem());
node.setRenderable(renderable);
node.setParent(anchorNode);
arFragment.getArSceneView().getScene().addChild(anchorNode);
node.select();
});
}
private void hostModel() {
if (cloudAnchor != null) {
arFragment.getArSceneView().getSession().hostCloudAnchor(cloudAnchor);
appAnchorState = AppAnchorState.HOSTING;
snackbarHelper.showMessage(this, "Now hosting anchor...");
} else {
snackbarHelper.showMessage(this, "No anchor to host, Please create an anchor...");
}
}
private void setCloudAnchor (Anchor newAnchor){
if (cloudAnchor != null){
cloudAnchor.detach();
}
cloudAnchor = newAnchor;
appAnchorState = AppAnchorState.NONE;
snackbarHelper.hide(this);
}
private void onUpdateFrame(FrameTime frameTime){
if (cloudAnchor != null) {
checkUpdatedAnchor();
}
}
private synchronized void checkUpdatedAnchor(){
if (appAnchorState != AppAnchorState.HOSTING){
return;
}
Anchor.CloudAnchorState cloudState = cloudAnchor.getCloudAnchorState();
if (appAnchorState == AppAnchorState.HOSTING) {
if (cloudState.isError()) {
snackbarHelper.showMessageWithDismiss(this, "Error hosting anchor.. "
+ cloudState);
appAnchorState = AppAnchorState.NONE;
} else if (cloudState == Anchor.CloudAnchorState.SUCCESS) {
snackbarHelper.showMessageWithDismiss(this, "Anchor hosted with id "
+ cloudAnchor.getCloudAnchorId());
appAnchorState = AppAnchorState.HOSTED;
}
}
}
public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
String openGlVersionString =
((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
.getDeviceConfigurationInfo()
.getGlEsVersion();
if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
Log.e(TAG, "Sceneform requires OpenGL ES 3.0 later");
Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
.show();
activity.finish();
return false;
}
return true;
}
}
apply plugin: 'com.android.application'
apply plugin: 'jacoco'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.arproject.russell.ar_t"
minSdkVersion 24
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
testCoverageEnabled true
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support:support-v4:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
testImplementation 'org.mockito:mockito-core:2.8.9'
implementation 'com.android.support:multidex:1.0.3'
/* dagger dependency for DI*/
implementation "com.google.dagger:dagger:2.13"
annotationProcessor "com.google.dagger:dagger-compiler:2.13"
compileOnly 'javax.annotation:jsr250-api:1.0'
implementation 'javax.inject:javax.inject:1'
/*Retrofit lib*/
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
/*RxJava lib*/
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation "io.reactivex.rxjava2:rxjava:2.1.8"
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
/* LiveData lib*/
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:runtime:1.1.1"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
// Room DB
implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
//Design
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:appcompat-v7:27.1.1'
//Circle Image View
implementation 'de.hdodenhof:circleimageview:2.2.0'
// ARCore
implementation 'com.google.ar:core:1.5.0'
// SceneForm
// Provides ArFragment, and other UX resources.
implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.5.1'
implementation 'com.google.ar.sceneform:assets:1.5.1'
//Preferences Screen
implementation 'com.android.support:preference-v7:27.1.1'
implementation 'com.android.support:preference-v14:27.1.1'
// Glide - An image loading and caching library for Android
// https://github.com/bumptech/glide
implementation 'com.github.bumptech.glide:glide:4.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
// CardView
implementation "com.android.support:cardview-v7:27.1.1"
//RecyclerView
implementation 'com.android.support:recyclerview-v7:27.1.1'
// ViewPager Arrow Indicator
implementation 'com.github.sembozdemir:ViewPagerArrowIndicator:1.0.0'
implementation 'com.mikhaellopez:circularimageview:3.0.2'
}
apply plugin: 'com.google.ar.sceneform.plugin'
sceneform.asset('sampledata/models/andy.obj',
'default',
'sampledata/models/andy.sfa',
'src/main/res/raw/andy')
package com.arproject.russell.ar_t.ar;
import com.google.ar.core.Config;
import com.google.ar.core.Session;
import com.google.ar.sceneform.ux.ArFragment;
public class CustomArFragment extends ArFragment {
@Override
protected Config getSessionConfiguration(Session session) {
Config config = super.getSessionConfiguration(session);
config.setCloudAnchorMode(Config.CloudAnchorMode.ENABLED);
return config;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment