The memo blog.

About programming language and useful library.

Vuforia-II: Camera(III)

| Comments

在經過前面一篇Vuforia-II: Camera(II)我們trace到了InitQCARTask()接下來就是updateApplicationStatus(APPSTATUS_INIT_APP_AR);case APPSTATUS_INIT_APP_AR:當中,主要就這段initApplicationAR();那我們來看一下這段function裡面的內容。

cameraDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 /** Initializes AR application components. */
  private void initApplicationAR() {
      // Do application initialization in native code (e.g. registering
      // callbacks, etc.):
      initApplicationNative(mScreenWidth, mScreenHeight);

      // Create OpenGL ES view:
      int depthSize = 16;
      int stencilSize = 0;
      boolean translucent = QCAR.requiresAlpha();

      mGlView = new QCARSampleGLView(this);
      mGlView.init(mQCARFlags, translucent, depthSize, stencilSize);

      mRenderer = new cameraDemoRenderer();
      mRenderer.mActivity = this;
      mGlView.setRenderer(mRenderer);

      LayoutInflater inflater = LayoutInflater.from(this);
      mUILayout = (RelativeLayout) inflater.inflate(R.layout.activity_main,
              null, false);

      mUILayout.setVisibility(View.VISIBLE);
      mUILayout.setBackgroundColor(Color.BLACK);

      // Gets a reference to the loading dialog
      mLoadingDialogContainer = mUILayout
              .findViewById(R.id.loading_indicator);

      // Shows the loading indicator at start
      loadingDialogHandler.sendEmptyMessage(SHOW_LOADING_DIALOG);

      // Adds the inflated layout to the view
      addContentView(mUILayout, new LayoutParams(LayoutParams.MATCH_PARENT,
              LayoutParams.MATCH_PARENT));
  }

看來我們又遇到了native code就是initApplicationNative(mScreenWidth, mScreenHeight);在這段native code當中我們可以知道它需要的參數為mScreenWidth, mScreenHeight,這初始化內容從前面一篇提到的initApplication()階段已經取得了當前的ScreenWidth以及Height所以除非出錯,否則應該是非0然而在這段native code當中似乎進行了不少事情。我想整個最讓我覺得厲害的應該就是jclass activityClass = env->GetObjectClass(obj);這部分是個Android jni當中,jnijava class溝通的一個方式,但是主要也是用來initialization texture所用,首先在java clas當中,有一個code是關於texture的,主要是讀取。那在這邊要解釋起來似乎利用圖說明會比較好,之後可以開個這種教學文,圖文並茂。簡單說就是首先在javainitialization的時候,會有一段是private void loadTextures(),在onCreate()的階段出現的。這邊會去讀取apk底下的texture資料。接著還有另外一個function是用來取得總共讀取的幾個texture就是public int getTextureCount()這邊的意思就是,在jni底下,先去尋找呼叫這段jniclass然後尋找這個class有沒有methodgetTextureCount,第三個參數()I這個部份可以參考這邊這個Blog有針對這些參數進行說明。那我們可以知道它主要就是來去取得這個classmethod那這個class的作用就是取得總共有幾個texture接著繼續呼叫用來init進行繪圖所需要的texture這部分等以後對OpenGL比較熟悉的時候再來慢慢研究好了。總之我們知道這段是用來init繪圖用的texture

cameraDemo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative(
                            JNIEnv* env, jobject obj, jint width, jint height)
{
    LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative");

    // Store screen dimensions
    screenWidth = width;
    screenHeight = height;

    // Handle to the activity class:
    jclass activityClass = env->GetObjectClass(obj);

    jmethodID getTextureCountMethodID = env->GetMethodID(activityClass,
                                                    "getTextureCount", "()I");
    if (getTextureCountMethodID == 0)
    {
        LOG("Function getTextureCount() not found.");
        return;
    }

    textureCount = env->CallIntMethod(obj, getTextureCountMethodID);
    if (!textureCount)
    {
        LOG("getTextureCount() returned zero.");
        return;
    }

    textures = new Texture*[textureCount];

    jmethodID getTextureMethodID = env->GetMethodID(activityClass,
        "getTexture", "(I)Lcom/qualcomm/QCARSamples/ImageTargets/Texture;");

    if (getTextureMethodID == 0)
    {
        LOG("Function getTexture() not found.");
        return;
    }

    // Register the textures
    for (int i = 0; i < textureCount; ++i)
    {

        jobject textureObject = env->CallObjectMethod(obj, getTextureMethodID, i);
        if (textureObject == NULL)
        {
            LOG("GetTexture() returned zero pointer");
            return;
        }

        textures[i] = Texture::create(env, textureObject);
    }
    LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative finished");
}

inittexture之後底下的工作大部份就是在初始化一些視窗元件等等,還有把View加入到addContentView()當中,我想應該可以先跳過了。接著進入updateApplicationStatus(APPSTATUS_LOAD_TRACKER);,在case APPSTATUS_LOAD_TRACKER:當中,主要就是進行mLoadTrackerTask = new LoadTrackerTask(); mLoadTrackerTask.execute();另外開個Task來讀取Tracker,在這邊主要的程式碼為:

cameraDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    /** An async task to load the tracker data asynchronously. */
    private class LoadTrackerTask extends AsyncTask<Void, Integer, Boolean>
    {
        protected Boolean doInBackground(Void... params)
        {
            // Prevent the onDestroy() method to overlap:
            synchronized (mShutdownLock)
            {
                // Load the tracker data set:
                return (loadTrackerData() > 0);
            }
        }

        protected void onPostExecute(Boolean result)
        {
            DebugLog.LOGD("LoadTrackerTask::onPostExecute: execution " +
                        (result ? "successful" : "failed"));

            if (result)
            {
                // The stones and chips data set is now active:
                mIsStonesAndChipsDataSetActive = true;

                // Done loading the tracker, update application status:
                updateApplicationStatus(APPSTATUS_INITED);
            }
            else
            {
                // Create dialog box for display error:
                AlertDialog dialogError = new AlertDialog.Builder
                (
                    ImageTargets.this
                ).create();

                dialogError.setButton
                (
                    DialogInterface.BUTTON_POSITIVE,
                    "Close",
                    new DialogInterface.OnClickListener()
                    {
                        public void onClick(DialogInterface dialog, int which)
                        {
                            // Exiting application:
                            System.exit(1);
                        }
                    }
                );

                // Show dialog box with error message:
                dialogError.setMessage("Failed to load tracker data.");
                dialogError.show();
            }
        }
    }

在主要的protected Boolean doInBackground(Void... params)當中只有進行一件事情,就是return (loadTrackerData() > 0);,這邊又是另外一個native code,接著就一樣跳進去這邊來看看吧。

cameraDemo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
JNIEXPORT int JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_loadTrackerData(JNIEnv *, jobject)
{
    LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_loadTrackerData");

    // Get the image tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::ImageTracker* imageTracker = static_cast<QCAR::ImageTracker*>(
                    trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER));
    if (imageTracker == NULL)
    {
        LOG("Failed to load tracking data set because the ImageTracker has not"
            " been initialized.");
        return 0;
    }

    // Create the data sets:
    dataSetStonesAndChips = imageTracker->createDataSet();
    if (dataSetStonesAndChips == 0)
    {
        LOG("Failed to create a new tracking data.");
        return 0;
    }

    dataSetTarmac = imageTracker->createDataSet();
    if (dataSetTarmac == 0)
    {
        LOG("Failed to create a new tracking data.");
        return 0;
    }

    // Load the data sets:
    if (!dataSetStonesAndChips->load("StonesAndChips.xml", QCAR::DataSet::STORAGE_APPRESOURCE))
    {
        LOG("Failed to load data set.");
        return 0;
    }

    if (!dataSetTarmac->load("Tarmac.xml", QCAR::DataSet::STORAGE_APPRESOURCE))
    //if (!dataSetTarmac->load("FunnyDB.xml", QCAR::DataSet::STORAGE_APPRESOURCE))
    {
        LOG("Failed to load data set.");
        return 0;
    }

    // Activate the data set:
    if (!imageTracker->activateDataSet(dataSetStonesAndChips))
    {
        LOG("Failed to activate data set.");
        return 0;
    }

    LOG("Successfully loaded and activated data set.");
    return 1;
}

似乎在進行TrackerManager操作的時候,都要先getInstance()似乎要先記住了。在這邊主要就是讀取外部的DataSet這些宣告在一開始的時候就有出現。似乎好像也沒有什麼特別的,如果要詳細說明應該只要查查API應該都有,這是DataSetAPI說明。我想這邊應該是可以重複利用才對。

1
2
QCAR::DataSet* dataSetStonesAndChips    = 0;
QCAR::DataSet* dataSetTarmac            = 0;

在看完這邊之後,接下來就是進入updateApplicationStatus(APPSTATUS_INITED);case APPSTATUS_INITED:當中,似乎是準備開始進入跑跑階段了。在這個階段當中,主要進行的onQCARInitializedNative();剩下的就是System.gc()和加入GLViewaddContentView()當中,接下來我們看一下onQCARInitializedNative()裡面是在執行哪些事情。

cameraDemo.cpp
1
2
3
4
5
6
7
8
9
10
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_onQCARInitializedNative(JNIEnv *, jobject)
{
    // Register the update callback where we handle the data set swap:
    QCAR::registerCallback(&updateCallback);

    // Comment in to enable tracking of up to 2 targets simultaneously and
    // split the work over multiple frames:
    // QCAR::setHint(QCAR::HINT_MAX_SIMULTANEOUS_IMAGE_TARGETS, 2);
}

結果這邊只是QCAR::registerCallback(&updateCallback);查查一下API,說明寫著void QCAR_API QCAR::registerCallback( UpdateCallback *object )以及 Registers an object to be called when new tracking data is available.然而這邊的updateCallback是一個class

1
ImageTargets_UpdateCallback updateCallback;
cameraDemo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Object to receive update callbacks from QCAR SDK
class ImageTargets_UpdateCallback : public QCAR::UpdateCallback
{
    virtual void QCAR_onUpdate(QCAR::State& /*state*/)
    {
        if (switchDataSetAsap)
        {
            switchDataSetAsap = false;

            // Get the image tracker:
            QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
            QCAR::ImageTracker* imageTracker = static_cast<QCAR::ImageTracker*>(
                trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER));
            if (imageTracker == 0 || dataSetStonesAndChips == 0 || dataSetTarmac == 0 ||
                imageTracker->getActiveDataSet() == 0)
            {
                LOG("Failed to switch data set.");
                return;
            }

            if (imageTracker->getActiveDataSet() == dataSetStonesAndChips)
            {
                imageTracker->deactivateDataSet(dataSetStonesAndChips);
                imageTracker->activateDataSet(dataSetTarmac);
            }
            else
            {
                imageTracker->deactivateDataSet(dataSetTarmac);
                imageTracker->activateDataSet(dataSetStonesAndChips);
            }
        }
    }
};

看了一下,原來是用來切換DataSet用的,在主要的class當中會有一段是呼叫menu然後選擇另外一個DataSet所使用的也是native code就只有呼叫:

cameraDemo.cpp
1
2
3
4
5
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_switchDatasetAsap(JNIEnv *, jobject)
{
    switchDataSetAsap = true;
}

所以註冊這個callback應該只是用來監控DataSet的變化。寫到這邊,突然覺得越寫越沒力氣。。。全部跑完之後好像是最後一個階段了updateApplicationStatus(APPSTATUS_CAMERA_RUNNING);之後的階段似乎都是在處理停止和結束的事情。在case APPSTATUS_CAMERA_RUNNING:當中,似乎只有進行startCamera();呼叫native code,接著就來看看這裡面到底執行哪些事情。

cameraDemo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_startCamera(JNIEnv *,
                                                                         jobject)
{
    LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_startCamera");

    // Select the camera to open, set this to QCAR::CameraDevice::CAMERA_FRONT 
    // to activate the front camera instead.
    QCAR::CameraDevice::CAMERA camera = QCAR::CameraDevice::CAMERA_DEFAULT;

    // Initialize the camera:
    if (!QCAR::CameraDevice::getInstance().init(camera))
        return;

    // Configure the video background
    configureVideoBackground();

    // Select the default mode:
    if (!QCAR::CameraDevice::getInstance().selectVideoMode(
                                QCAR::CameraDevice::MODE_DEFAULT))
        return;

    // Start the camera:
    if (!QCAR::CameraDevice::getInstance().start())
        return;

    // Uncomment to enable flash
    //if(QCAR::CameraDevice::getInstance().setFlashTorchMode(true))
    //   LOG("IMAGE TARGETS : enabled torch");

    // Uncomment to enable infinity focus mode, or any other supported focus mode
    // See CameraDevice.h for supported focus modes
    //if(QCAR::CameraDevice::getInstance().setFocusMode(QCAR::CameraDevice::FOCUS_MODE_INFINITY))
    //   LOG("IMAGE TARGETS : enabled infinity focus");

    // Start the tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::Tracker* imageTracker = trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER);
    if(imageTracker != 0)
        imageTracker->start();
}

首先,先來查一下QCARCamera API,可以知道enum QCAR::CameraDevice::CAMERA型態為enum總共有三個參數:

1
2
3
4
Enumerator:
CAMERA_DEFAULT: Default camera device. Usually BACK.
CAMERA_BACK   : Rear facing camera.
CAMERA_FRONT  : Front facing camera.

好像也沒有什麼特別的,就是指定說我要後置鏡頭這樣。DEFAULT通常都是後置。接著就是取得Instance然後init()接下來又是呼叫另外一個native code老實說,感覺就像是在設定螢幕寬高的樣子,只是可能還有帶著3D物件所需要的參數?先看一下QCAR::VideoBackgroundConfig config;API好了。裡面主要有5個Public Attributes,分別為:

1
2
3
4
5
bool QCAR::VideoBackgroundConfig::mEnabled
bool QCAR::VideoBackgroundConfig::mSynchronous
Vec2I QCAR::VideoBackgroundConfig::mPosition
Vec2I QCAR::VideoBackgroundConfig::mSize
VIDEO_BACKGROUND_REFLECTION QCAR::VideoBackgroundConfig::mReflection

在這裡面也有說明可以參考,那在這段code主要的設定好像只有前面4個,在mEnabled的說明為:Enables/disables rendering of the video background.看樣子是設定要不要Rendering物件?接著就是mSynchronous,說明為Enables/disables synchronization of render and camera frame rate.又有一些補充說明If synchronization is enabled the SDK will attempt to match the rendering frame rate with the camera frame rate. This may result in a performance gain as potentially redundant render calls are avoided. Enabling this is not recommended if your augmented content needs to be animated at a rate higher than the rate at which the camera delivers frames.看樣子應該是rander的物件是不是要和frame rate同步。上面寫說,如果你的物件是個超級animated很會動,可能會很吃資源,建議不要同步。假設你的動畫需要100 fps你去和30 fps的同步應該會爆炸。在這邊因為用的是靜態茶壺,應該不會怎樣才對,除非茶壺需要自體轉動,不過好像也沒差?接著就是mPosition,說明為Relative position of the video background in the render target in pixels. Describes the offset of the center of video background to the center of the screen (viewport) in pixels. A value of (0,0) centers the video background, whereas a value of (-10,15) moves the video background 10 pixels to the left and 15 pixels upwards.看樣子應該就是offset就是通常來說,物件都會RenderTarget的中央,那這個offset就是可以偏移一下這樣。那在這邊是不要偏移,所以就都用0.0f了,f沒意外應該是代表float 接著好像就是設定mSize說明為Width and height of the video background in pixels.似乎就是background的寬高,然後是要pixels,底下說明了Using the device's screen size for this parameter scales the image to fullscreen. Notice that if the camera's aspect ratio is different than the screen's aspect ratio this will create a non-uniform stretched image.看樣子應該是要把camerasize能夠和screen size有著相同的比例?應該是,否則就不會去取得VideoMode了,這是VideoMode的API,果然就是取得Camera的,但是它只有三種Mode,老實說,也沒有出現每個MODE的解析度,不知道它是怎樣決定的,反正先知道它的功能就好了。最後終於結束了,這樣就可以動了?寫到這邊我只覺得,似乎可以拉出來變成Template的地方還挺多的,之後再想想要怎樣重寫或是自己寫一個這東西出來才可以比較知道整個從無到有的步驟流程該怎樣寫。

1
2
3
MODE_DEFAULT: Default camera mode.
MODE_OPTIMIZE_SPEED: Fast camera mode.
MODE_OPTIMIZE_QUALITY: High-quality camera mode.
cameraDemo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
void
configureVideoBackground()
{
    // Get the default video mode:
    QCAR::CameraDevice& cameraDevice = QCAR::CameraDevice::getInstance();
    QCAR::VideoMode videoMode = cameraDevice.
                                getVideoMode(QCAR::CameraDevice::MODE_DEFAULT);


    // Configure the video background
    QCAR::VideoBackgroundConfig config;
    config.mEnabled = true;
    config.mSynchronous = true;
    config.mPosition.data[0] = 0.0f;
    config.mPosition.data[1] = 0.0f;

    if (isActivityInPortraitMode)
    {
        //LOG("configureVideoBackground PORTRAIT");
        config.mSize.data[0] = videoMode.mHeight
                                * (screenHeight / (float)videoMode.mWidth);
        config.mSize.data[1] = screenHeight;

        if(config.mSize.data[0] < screenWidth)
        {
            LOG("Correcting rendering background size to handle missmatch between screen and video aspect ratios.");
            config.mSize.data[0] = screenWidth;
            config.mSize.data[1] = screenWidth *
                              (videoMode.mWidth / (float)videoMode.mHeight);
        }
    }
    else
    {
        //LOG("configureVideoBackground LANDSCAPE");
        config.mSize.data[0] = screenWidth;
        config.mSize.data[1] = videoMode.mHeight
                            * (screenWidth / (float)videoMode.mWidth);

        if(config.mSize.data[1] < screenHeight)
        {
            LOG("Correcting rendering background size to handle missmatch between screen and video aspect ratios.");
            config.mSize.data[0] = screenHeight
                                * (videoMode.mWidth / (float)videoMode.mHeight);
            config.mSize.data[1] = screenHeight;
        }
    }

    LOG("Configure Video Background : Video (%d,%d), Screen (%d,%d), mSize (%d,%d)", videoMode.mWidth, videoMode.mHeight, screenWidth, screenHeight, config.mSize.data[0], config.mSize.data[1]);

    // Set the config:
    QCAR::Renderer::getInstance().setVideoBackgroundConfig(config);
}
cameraDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 /**
  * NOTE: this method is synchronized because of a potential concurrent
  * access by ImageTargets::onResume() and InitQCARTask::onPostExecute().
  */
  private synchronized void updateApplicationStatus(int appStatus) {
      // Exit if there is no change in status:
      if (mAppStatus == appStatus)
          return;

      // Store new status value:
      mAppStatus = appStatus;

      // Execute application state-specific actions:
      switch (mAppStatus) {
      case APPSTATUS_INIT_APP:
          // Initialize application elements that do not rely on QCAR
          // initialization:
          initApplication();

          // Proceed to next application initialization status:
          updateApplicationStatus(APPSTATUS_INIT_QCAR);
          break;

      case APPSTATUS_INIT_QCAR:
          // Initialize QCAR SDK asynchronously to avoid blocking the
          // main (UI) thread.
          //
          // NOTE: This task instance must be created and invoked on the
          // UI thread and it can be executed only once!
          try {
              mInitQCARTask = new InitQCARTask();
              mInitQCARTask.execute();
          } catch (Exception e) {
              DebugLog.LOGE("cameraDemo", "Initializing QCAR SDK failed");
          }
          break;

      case APPSTATUS_INIT_TRACKER:
          // Initialize the ImageTracker:
          if (initTracker() > 0) {
              // Proceed to next application initialization status:
              updateApplicationStatus(APPSTATUS_INIT_APP_AR);
          }
          break;

      case APPSTATUS_INIT_APP_AR:
          // Initialize Augmented Reality-specific application elements
          // that may rely on the fact that the QCAR SDK has been
          // already initialized:
          initApplicationAR();

          // Proceed to next application initialization status:
          updateApplicationStatus(APPSTATUS_LOAD_TRACKER);
          break;

      case APPSTATUS_LOAD_TRACKER:
          // Load the tracking data set:
          //
          // NOTE: This task instance must be created and invoked on the
          // UI thread and it can be executed only once!
          try {
              mLoadTrackerTask = new LoadTrackerTask();
              mLoadTrackerTask.execute();
          } catch (Exception e) {
              DebugLog.LOGE("cameraDemo", "Loading tracking data set failed");
          }
          break;

      case APPSTATUS_INITED:
          // Hint to the virtual machine that it would be a good time to
          // run the garbage collector:
          //
          // NOTE: This is only a hint. There is no guarantee that the
          // garbage collector will actually be run.
          System.gc();

          // Native post initialization:
          onQCARInitializedNative();

          // Activate the renderer:
          mRenderer.mIsActive = true;

          // Now add the GL surface view. It is important
          // that the OpenGL ES surface view gets added
          // BEFORE the camera is started and video
          // background is configured.
          addContentView(mGlView, new LayoutParams(LayoutParams.MATCH_PARENT,
                  LayoutParams.MATCH_PARENT));

          // Sets the UILayout to be drawn in front of the camera
          // mUILayout.bringToFront();

          // Start the camera:
          updateApplicationStatus(APPSTATUS_CAMERA_RUNNING);

          break;

      case APPSTATUS_CAMERA_STOPPED:
          // Call the native function to stop the camera:
          stopCamera();
          break;

      case APPSTATUS_CAMERA_RUNNING:
          // Call the native function to start the camera:
          startCamera();

          // Hides the Loading Dialog
          // loadingDialogHandler.sendEmptyMessage(HIDE_LOADING_DIALOG);

          // Sets the layout background to transparent
          // mUILayout.setBackgroundColor(Color.TRANSPARENT);

          // Set continuous auto-focus if supported by the device,
          // otherwise default back to regular auto-focus mode.
          // This will be activated by a tap to the screen in this
          // application.
          if (!setFocusMode(FOCUS_MODE_CONTINUOUS_AUTO)) {
              mContAutofocus = false;
              setFocusMode(FOCUS_MODE_NORMAL);
          } else {
              mContAutofocus = true;
          }
          break;

      default:
          throw new RuntimeException("Invalid application state");
      }
  }

Comments