org.artoolkit.ar.base.ARActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.artoolkit.ar.base.ARActivity.java

Source

/*
 *  ARActivity.java
 *  ARToolKit5
 *
 *  This file is part of ARToolKit.
 *
 *  ARToolKit is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  ARToolKit 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with ARToolKit.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  As a special exception, the copyright holders of this library give you
 *  permission to link this library with independent modules to produce an
 *  executable, regardless of the license terms of these independent modules, and to
 *  copy and distribute the resulting executable under terms of your choice,
 *  provided that you also meet, for each linked independent module, the terms and
 *  conditions of the license of that module. An independent module is a module
 *  which is neither derived from nor based on this library. If you modify this
 *  library, you may extend this exception to your version of the library, but you
 *  are not obligated to do so. If you do not wish to do so, delete this exception
 *  statement from your version.
 *
 *  Copyright 2015 Daqri, LLC.
 *  Copyright 2011-2015 ARToolworks, Inc.
 *
 *  Author(s): Julian Looser, Philip Lamb
 *
 */

package org.artoolkit.ar.base;

import org.artoolkit.ar.base.camera.CameraEventListener;
import org.artoolkit.ar.base.camera.CameraPreferencesActivity;
import org.artoolkit.ar.base.camera.CaptureCameraPreview;
import org.artoolkit.ar.base.rendering.ARRenderer;
import org.artoolkit.ar.base.rendering.GDXRenderer;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Build;
//import android.os.AsyncTask;
//import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManager;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.Toast;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.badlogic.gdx.backends.android.surfaceview.FillResolutionStrategy;

/**
 * An activity which can be subclassed to create an AR application. ARActivity handles almost all of 
 * the required operations to create a simple augmented reality application. 
 * 
 * ARActivity automatically creates a camera preview surface and an OpenGL surface view, and 
 * arranges these correctly in the user interface.The subclass simply needs to provide a FrameLayout 
 * object which will be populated with these UI components, using {@link supplyFrameLayout}.
 * 
 * To create a custom AR experience, the subclass should also provide a custom renderer using 
 * {@link supplyRenderer}. This allows the subclass to handle OpenGL drawing calls on its own.
 * 
 */

public abstract class ARActivity extends AndroidApplication implements CameraEventListener {

    /**
     * Android logging tag for this class.
     */
    protected final static String TAG = "ARActivity";

    /**
     * Camera preview which will provide video frames.
     */
    private CaptureCameraPreview preview;

    /**
     * GL surface to render the virtual objects    
     */
    private GLSurfaceView glView;

    /**
     *  Renderer to use. This is provided by the subclass using {@link supplyRenderer()}.
     */
    protected ARRenderer renderer;

    /**
     * Layout that will be filled with the camera preview and GL views. This is provided by the subclass using {@link supplyFrameLayout()}.
     */
    protected FrameLayout mainLayout;

    /**
      * For any square template (pattern) markers, the number of rows
      * and columns in the template. May not be less than 16 or more than AR_PATT_SIZE1_MAX.
      */
    protected int pattSize = 16;

    /**
      * For any square template (pattern) markers, the maximum number
      * of markers that may be loaded for a single matching pass. Must be > 0.
      */
    protected int pattCountMax = 25;

    private boolean firstUpdate = false;

    private AndroidApplicationConfiguration config;

    private GDXRenderer gdxRenderer;

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        config = new AndroidApplicationConfiguration();

        //      initialize(new GDXRenderer(), config);
        config.a = 8;
        config.r = 8;
        config.g = 8;
        config.b = 8;
        config.depth = 16;
        config.stencil = 0;
        gdxRenderer = new GDXRenderer();
        initializeForView(gdxRenderer, config);

        // This needs to be done just only the very first time the application is run,
        // or whenever a new preference is added (e.g. after an application upgrade).
        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);

        // Correctly configures the activity window for running AR in a layer
        // on top of the camera preview. This includes entering 
        // fullscreen landscape mode and enabling transparency. 
        boolean needActionBar = false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                if (!ViewConfiguration.get(this).hasPermanentMenuKey())
                    needActionBar = true;
            } else {
                needActionBar = true;
            }
        }
        if (needActionBar) {
            requestWindowFeature(Window.FEATURE_ACTION_BAR);
        } else {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }
        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        AndroidUtils.reportDisplayInformation(this);
    }

    /**
     * Allows subclasses to supply a custom {@link Renderer}.
     * @return The {@link Renderer} to use.
     */
    protected abstract ARRenderer supplyRenderer();

    /**
     * Allows subclasses to supply a {@link FrameLayout} which will be populated
     * with a camera preview and GL surface view.
     * @return The {@link FrameLayout} to use.
     */
    protected abstract FrameLayout supplyFrameLayout();

    @Override
    protected void onStart() {

        super.onStart();

        Log.i(TAG, "Activity starting.");

        if (ARToolKit.getInstance().initialiseNativeWithOptions(this.getCacheDir().getAbsolutePath(), pattSize,
                pattCountMax) == false) { // Use cache directory for Data files.

            new AlertDialog.Builder(this)
                    .setMessage("The native library is not loaded. The application cannot continue.")
                    .setTitle("Error").setCancelable(true)
                    .setNeutralButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            finish();
                        }
                    }).show();

            return;
        }

        mainLayout = supplyFrameLayout();
        if (mainLayout == null) {
            Log.e(TAG, "Error: supplyFrameLayout did not return a layout.");
            return;
        }

        renderer = supplyRenderer();
        if (renderer == null) {
            Log.e(TAG, "Error: supplyRenderer did not return a renderer.");
            // No renderer supplied, use default, which does nothing
            renderer = new ARRenderer(this, config,
                    (config.resolutionStrategy == null) ? new FillResolutionStrategy() : config.resolutionStrategy);
        }

    }

    @SuppressWarnings("deprecation") // FILL_PARENT still required for API level 7 (Android 2.1)
    @Override
    public void onResume() {
        //Log.i(TAG, "onResume()");
        super.onResume();

        // Create the camera preview
        preview = new CaptureCameraPreview(this, this);

        Log.i(TAG, "CaptureCameraPreview created");

        // Create the GL view
        //       glView = new GLSurfaceView(this);

        glView = (GLSurfaceView) this.graphics.getView();

        // Set to use OpenGL ES 2.0
        //      glView.setEGLContextClientVersion(2);

        //      glView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        glView.getHolder().setFormat(PixelFormat.TRANSLUCENT); // Needs to be a translucent surface so the camera preview shows through.
        glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // Only render when we have a frame (must call requestRender()).
        glView.setZOrderMediaOverlay(true); // Request that GL view's SurfaceView be on top of other SurfaceViews (including CameraPreview's SurfaceView).
        //
        //      Log.i(TAG, "GLSurfaceView created");

        // Add the views to the interface
        mainLayout.addView(preview, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        mainLayout.addView(glView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

        Log.i(TAG, "Views added to main layout.");

        if (glView != null)
            glView.onResume();
    }

    @Override
    protected void onPause() {
        //Log.i(TAG, "onPause()");
        super.onPause();

        if (glView != null)
            glView.onPause();

        // System hardware must be released in onPause(), so it's available to
        // any incoming activity. Removing the CameraPreview will do this for the
        // camera. Also do it for the GLSurfaceView, since it serves no purpose
        // with the camera preview gone.
        mainLayout.removeView(glView);
        mainLayout.removeView(preview);
    }

    @Override
    public void onStop() {
        Log.i(TAG, "Activity stopping.");

        super.onStop();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.options, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.settings) {
            startActivity(new Intent(this, CameraPreferencesActivity.class));
            return true;
        } else {
            return super.onOptionsItemSelected(item);
        }
    }

    /**
     * Returns the camera preview that is providing the video frames.
     * @return The camera preview that is providing the video frames.
     */
    public CaptureCameraPreview getCameraPreview() {
        return preview;
    }

    /**
     * Returns the GL surface view.
     * @return The GL surface view.
     */
    public GLSurfaceView getGLView() {
        return glView;
    }

    @Override
    public void cameraPreviewStarted(int width, int height, int rate, int cameraIndex,
            boolean cameraIsFrontFacing) {

        if (ARToolKit.getInstance().initialiseAR(width, height, "Data/camera_para.dat", cameraIndex,
                cameraIsFrontFacing)) { // Expects Data to be already in the cache dir. This can be done with the AssetUnpacker.
            Log.i(TAG, "Camera initialised");
        } else {
            // Error
            Log.e(TAG, "Error initialising camera. Cannot continue.");
            finish();
        }

        Toast.makeText(this, "Camera settings: " + width + "x" + height + "@" + rate + "fps", Toast.LENGTH_SHORT)
                .show();

        firstUpdate = true;
    }

    //
    // At present, the underlying ARWrapper is not thread-safe,
    // so this multi-threaded version is set aside in favour of a single-threaded version
    //
    /*
    private class ConvertAndDetectTask extends AsyncTask<byte[], Void, Boolean> {
        
       @Override
      protected Boolean doInBackground(byte[]... frame) {         
     return (ARToolKit.getInstance().convertAndDetect(frame[0]));         
      }
           
       @Override
       protected void onPostExecute(Boolean result) {
          
     if (firstUpdate) {
            
        firstUpdate = false;
            
        // ARToolKit has been initialised. The renderer can now add markers, etc...
        if (renderer.configureARScene()) {
           Log.i(TAG, "Scene configured successfully");
        } else { 
           // Error
           Log.e(TAG, "Error configuring scene. Cannot continue.");
           finish();
        }
     }
         
         
     // Update the renderer as the frame has changed
     if (glView != null) glView.requestRender();
         
     onFrameProcessed();
         
      }
          
    }
        
    ConvertAndDetectTask task;
        
    @Override
    public void cameraPreviewFrame(byte[] frame) {
        
      if (task == null || task.getStatus() == Status.FINISHED) {      
     task = new ConvertAndDetectTask();
     task.execute(frame);
      }
    }
        
    boolean cleanupFlag = false;
        
    public void onFrameProcessed() {
       if (cleanupFlag) {
      ARToolKit.getInstance().cleanup();
      cleanupFlag = false;
       }
    }
        
    @Override
    public void cameraPreviewStopped() {
      if (task != null && task.getStatus() != Status.FINISHED) {
     cleanupFlag = true;   
      } else {
     ARToolKit.getInstance().cleanup();
      }
    }   
    */

    @Override
    public void cameraPreviewFrame(byte[] frame) {

        if (firstUpdate) {
            // ARToolKit has been initialised. The renderer can now add markers, etc...
            //         if (renderer.configureARScene()) {
            if (gdxRenderer.configureARScene()) {
                Log.i(TAG, "Scene configured successfully");
            } else {
                // Error
                Log.e(TAG, "Error configuring scene. Cannot continue.");
                finish();
            }
            firstUpdate = false;
        }

        if (ARToolKit.getInstance().convertAndDetect(frame)) {

            // Update the renderer as the frame has changed
            if (glView != null)
                glView.requestRender();

            onFrameProcessed();
        }

    }

    public void onFrameProcessed() {
    }

    @Override
    public void cameraPreviewStopped() {
        ARToolKit.getInstance().cleanup();
    }

    protected void showInfo() {

        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);

        dialogBuilder.setMessage("ARToolKit Version: " + NativeInterface.arwGetARToolKitVersion());

        dialogBuilder.setCancelable(false);
        dialogBuilder.setPositiveButton("Close", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        });

        AlertDialog alert = dialogBuilder.create();
        alert.setTitle("ARToolKit");
        alert.show();

    }

}