Strange behaviour of AndAR(r205) sample code "nmatsui / AR_Speeker" on Android Studio

622 Views Asked by At

I've tried to replicate/migrade, the AndAR sample code Mr. Nobuyuki Matsui uploaded on GitHub, the "nmatsui / AR_Speeker", which can be downloaded from here, and I get a strange behaviour on Android Studio (0.4.0). The provided code works O.K. in Eclipse (Juno).

The problem is that I can compile without errors in Android Studio, but when I debug to my device, I get a black screen and no functionality at all - no camera preview - no model loading.

What is more, I get no fatal errors in logcat, as long I can interpret those error to my limited knowledge of java/android.

I am asking for someones help here, cause I need to make this code working on Android Studio, so I can build upon it an exercise app within a ~2 weeks deadline.

Does anyone has successfully tried to replicate/migrate the "nmatsui / AR_Speeker" for Android Studio, and can provide me with a GitHub link, maybe?

Thank you all in advance.

Here is the Project (structure):

enter image description here

Here is my Android Manifest (I got an error with the @drawable/icon, which I temporally fix by tools:ignore="MissingApplicationIcon"):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="jp.co.tis.stc"
  android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" android:maxSdkVersion="10"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" />
<supports-screens android:smallScreens="true"
    android:normalScreens="true"
    android:largeScreens="true"
    android:anyDensity="true" />

<application
    android:allowBackup="true"
    android:label="AR_Speaker"
    tools:ignore="MissingApplicationIcon">
    <activity android:name=".AR_SpeakerActivity"
              android:clearTaskOnLaunch="true"
              android:noHistory="true"
              android:screenOrientation="landscape"
              android:label="AR_Speaker">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

Here is the build.gradle.xml:

buildscript {
repositories {
    mavenCentral()
}
dependencies {
    classpath 'com.android.tools.build:gradle:0.7.+'
 }
}
apply plugin: 'android'
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile files('libs/AndAR.jar')}
android {
compileSdkVersion 19
buildToolsVersion "19.0.2"

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
    }

    instrumentTest.setRoot('tests')
   }
  }

Where I get a warning that "Cannot assign ArrayList<String> to "HashSet<Iterable<?>>' on line 23 and 24.

Here is the main activity AR_SpeakerActivity (all comments are in Japanese, that I cannot interpret!), where shows a warning that 'getWidth()' and 'getHeight()' is deprecated on line 123, 125. What should I do with that?

EDITED as Raghav Sood suggested in lines 123-124 (now 125-133). Still get black screen. Should I still use "Ratio" with this code snippet? How should I edit that?

package jp.co.tis.stc;

import android.app.ProgressDialog;
import android.hardware.Camera.Size;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.WindowManager;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import edu.dhbw.andar.ARToolkit;
import edu.dhbw.andar.AndARActivity;
import edu.dhbw.andobjviewer.graphics.LightingRenderer;
import jp.co.tis.stc.player.Elaine;
import jp.co.tis.stc.player.Porl;
import util.MarkerInfo;

public class AR_SpeakerActivity extends AndARActivity {
private static final float THRESHOLD = 50.0f;
// タップ位置とマーカー中心のズレの許容範囲 ピ   ク  セル)
private ARToolkit arToolkit;
private GestureDetector gd;
private SoundPool sp;
private float xRatio;
private float yRatio;

private List<PlayerBase> players = new ArrayList<PlayerBase>();

@SuppressWarnings("unchecked")
// AsyncTaskへplayersを可変引数として渡す際に、型パラメータが落ちるという警告を抑制
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    gd = new GestureDetector(this, onGestureListener); // タップを検出するDetector

    super.setNonARRenderer(new LightingRenderer());
    arToolkit = super.getArtoolkit();

    players.add(new Porl());
// マーカー・3Dモデル・音声・動作を定義したPlayer "Porl" を追加
    players.add(new Elaine());
// マーカー・3Dモデル・音声・動作を定義したPlayer "Elaine" を追加

    new ModelLoader().execute(players); // 非同期処理でPlayerを読み込み
}

@Override
protected void onResume() {
    super.onResume();
    // Activityが前面になったら音声再生用にSoundPoolを作成
    sp = new SoundPool(players.size(), AudioManager.STREAM_MUSIC, 0);
    sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
        // SoundPoolの初期化が完了した際に呼ばれるコールバック関数を定義
        @Override
        public void onLoadComplete(SoundPool sp, int soundId, int status) {
        Log.d("AR_Speaker", String.format("load complete soundId=%d:status=%d", soundId, status));
            for (PlayerBase player : players) {
                player.notifyLoadComplete(soundId);
// Playerに「このsoundIdの初期化が完了したよ」と通知
            }
        }
    });
    for (PlayerBase player : players) {
        player.loadSound(this, sp); // すべてのPlayerの音声を読み込む
    }
}

@Override
protected void onPause() {
    sp.release(); // Activityが背面にまわったらSoundPoolを解放
    super.onPause();
}

@Override
public void uncaughtException(Thread thread, Throwable ex) {
    Log.e("AR_Speaker", ex.getMessage());
    finish();
}

// 非同期処理でPlayerを読み込み
private class ModelLoader extends AsyncTask<List<PlayerBase>, Void, Void> {
    private ProgressDialog progressDialog;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // 非同期処理開始前にプログレスダイアログを表示
        progressDialog = new ProgressDialog(AR_SpeakerActivity.this);
         progressDialog.setMessage(AR_SpeakerActivity.this.getString(R.string.loading));
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.show();
    }

    @Override
    protected Void doInBackground(List<PlayerBase>... args) {
        try {
            for (PlayerBase player : args[0]) {
                // Playerの3DモデルをARObjectとしてARToolkitへ登録する
                arToolkit.registerARObject(player.getModel3d(getResources()));
            }
        } catch (Exception e) {
            Log.e("AR_Speaker", e.getMessage());
            finish();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
    // ARToolkitによって初期化されたcameraインスタンスから、カメラ座標系のパラメータを取得
        Size cameraSize = camera.getParameters().getPreviewSize();
        // WindowManagerから、スクリーン座標系のパラメータを取得
        Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
        // スクリーン座標系もカメラ座標系も左上隅が原点位置
        // カメラ座標系のX座標をスクリーン座標系のX座標へ変換する係数を計算

        // Edit code as Raghav Sood suggested/ 
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int height = metrics.heightPixels;
        int width = metrics.widthPixels;



        /**xRatio = (float) display.getWidth() / (float) cameraSize.width;
        // カメラ座標系のY座標をスクリーン座標系のY座標へ変換する係数を計算
        yRatio = (float) display.getHeight() / (float) cameraSize.height;
        // 非同期処理が終了したので、プログレスダイアログを消去*/

        progressDialog.dismiss();
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    return gd.onTouchEvent(event);
 }

// タップ処理
private final SimpleOnGestureListener onGestureListener = new  SimpleOnGestureListener() {
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        // タップされた位置をスクリーン座標系として取得
        float touchX = e.getX();
        float touchY = e.getY();
        // 認識しているすべてのマーカーの位置情報を取得
        Map<Integer, MarkerInfo> markerInfos = arToolkit.getMarkerInfos();
        for (MarkerInfo markerInfo : markerInfos.values()) {
            // カメラ座標系でのマーカー位置(マーカーの中心点)を取得
            float markerX = markerInfo.getPos()[0];
            float markerY = markerInfo.getPos()[1];

            // 認識しているマーカーの中心点をカメラ座標系からスクリーン座標系に変換し、
            // |マーカーの中心 - タップ位置| < THRESHOLD
            // であるかチェック
            if (Math.abs(markerX * xRatio - touchX) < THRESHOLD
                    && Math.abs(markerY * yRatio - touchY) <  THRESHOLD) {
                Log.d("AR_Speaker", String.format("marker %s is touched", markerInfo.getFileName()));
                for (PlayerBase player : players) {
            // すべてのPlayerに、「XXという名前のマーカーがタップされた」と通知
                    player.notifyTouch(new File(markerInfo.getFileName()).getName(), sp);
                }
            }
        }
        return true;
    }
};}

And here is the other activity PlayerBase:

package jp.co.tis.stc;

import java.io.BufferedReader;
import java.io.IOException;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.content.res.Resources;
import android.media.SoundPool;
import edu.dhbw.andobjviewer.graphics.Model3D;
import edu.dhbw.andobjviewer.models.Model;
import edu.dhbw.andobjviewer.parser.ObjParser;
import edu.dhbw.andobjviewer.parser.ParseException;
import edu.dhbw.andobjviewer.util.AssetsFileUtil;
import edu.dhbw.andobjviewer.util.BaseFileUtil;

public abstract class PlayerBase {
private static final double MARKER_WIDTH = 80.0;
private static final double[] MARKER_CENTER = new double[] { 0, 0 };

private final String modelFile;
private final String markerFile;
private final int voiceR;
private int soundId;
private boolean loaded = false;

protected boolean doAnimate = false;

public PlayerBase(String modelFile, String markerFile, int voiceR) {
    this.modelFile = modelFile;
    this.markerFile = markerFile;
    this.voiceR = voiceR;
}

// WaveFront形式の3Dモデルファイルを読み込み、ARToolkitが認識できる3Dモデルを構築して返す
public Model3D getModel3d(Resources resource) throws IOException, ParseException {
    BaseFileUtil fileUtil = new AssetsFileUtil(resource.getAssets());

    Model3D model3D = null;

    if (modelFile.endsWith(".obj")) {
        ObjParser parser = new ObjParser(fileUtil);
        if (fileUtil != null) {
            BufferedReader fileReader = fileUtil.getReaderFromName(modelFile);
            if (fileReader != null) {
                // Wavefront形式の3Dモデルファイルから3Dモデルを構築
                Model model = parser.parse("Model", fileReader);

                // 3Dモデルファイルとマーカーを指定して、ARToolkitへ登録するためのModel3Dオブジェクトを作成する
                model3D = new Model3D(model, markerFile, MARKER_WIDTH, MARKER_CENTER) {
                    private static final long serialVersionUID = 1L;

                    // Model3Dに仕掛けたフックの中身を定義
                    // 実際のanimate処理は、PlayerBaseを継承した具象クラスのanimateメソッドに実装することになる
                    @Override
                    protected void animate(GL10 gl) {
                        PlayerBase.this.animate(gl);
                    }
                };
            }
        }
    }
    return model3D;
}

// animate処理の抽象メソッド
protected abstract void animate(GL10 gl);

// SoundPoolへ音声をロードするメソッド
public void loadSound(Context context, SoundPool sp) {
    // res/rawに格納した音声ファイルを指定してロードすると、その音声のsoundIdが得られる
    // このメソッドは実際のロードが完成する前にリターンする
    soundId = sp.load(context, voiceR, 1);
}

// SoundPoolへの音声ロードが完成すると呼ばれるメソッド
public void notifyLoadComplete(int soundId) {
    // 自分のsoundIdのロードが完了したのならば、loadedをtrueにする
    if (this.soundId == soundId) loaded = true;
}

// マーカーがタップされた際に呼ばれるメソッド
public void notifyTouch(String fileName, SoundPool sp) {
    // 自分のマーカーがタップされたのならば、自分のsoundIdを指定して音声を再生し、doAnimateをtrueにする
    if (this.markerFile.equals(fileName) && loaded) {
        sp.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f);
        if (!this.doAnimate) this.doAnimate = true;
    }
} }
1

There are 1 best solutions below

2
On

Application is missing

This is a lint error, and happens because you don't seem to have any Java class extending Application, which is mentioned in your manifest. You can ignore this error as you have done, since this is not a mandatory part of the application.

build.gradle error

This is a known issue with Android Studio. Workarounds can be found in this question.

Deprecated methods

This is because getHeight() and getWidth() were deprecated in API 13. You can use DisplayMetrics:

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;