0

The title is a little vague, but the problem is a little tricky to explain, so here goes...

(In this context, as I do not fully have the vernacular down, I use the term "Child Activity" to mean an activity that was launched via an intent from a parent activity, within the same app.)

I build a simple, sample app to bring up the camera, and read a barcode. It works fine, until it is launched from another activity that I put in front of it. Then the autofocus does not work.

The SDK, google play libraries, versions do not have a bearing on the problem, since it does work in one activity, but not when launched by another.

Lastly, I did not deal with posting for "ActivityResult" in this example. This is not related to the issue I am experiencing.

I created a MainActivity. I did not include superflous classes like camera preview, layouts, icons, etc, as they are not needed to identify this problem. If necessary, and can make the a zip file available to anyone to download it. This is to only demonstrate the two activities.

package com.digitalcheck.barcodedecode;

import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.FrameLayout;
import android.widget.TextView;

import org.json.JSONObject;

public class MainActivity extends AppCompatActivity {

    public static final String EXTRA_REPLY = "extra.REPLY";

    private final Handler m_handler = new Handler();
    private TextView m_status;
    private boolean m_cameraPresent;
    private CameraPreview m_preview;

    private Camera m_camera;
    private FrameLayout m_cameraFrame;
    private Decode m_decode;
    private String m_data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        m_status = (TextView) findViewById(R.id.textView);
        m_cameraFrame = (FrameLayout) findViewById(R.id.camerapreview) ;
        m_camera = null;
        m_preview = null;
        m_decode = null;

        m_cameraPresent = getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
        if (m_cameraPresent == false) {
            m_status.setText("No Camera was found on this device");

        }
        ActivateCamera();
    }

    final Runnable UpdateScreen = new Runnable() {
        public void run() {
            int x;
            String user;
            String key, value;
            JSONObject jitem;
            m_cameraFrame.removeAllViews();
            m_status.setText(m_data);
        }
    };

    public void BarcodeData(String data) {
        m_data = data;
        m_handler.post(UpdateScreen);
    }

    final Runnable Refocus = new Runnable() {
        public void run() {
            m_camera.autoFocus(m_decode);
        }
    };
    public void TryAgain() {
        m_handler.post(Refocus);

    }
    /*************************************
     *  Overrides
     **************************************/
    @Override
    public void onPostResume() {
        super.onPostResume();
        if (m_camera == null) {
            ActivateCamera();
        }
        if (m_camera != null) {
            m_camera.autoFocus(m_decode);
        }
    }
    @Override
    public void onPause() {
        super.onPause();
        if (m_camera != null) {
            m_camera.release();
            m_camera = null;
            m_preview = null;
            m_decode = null;
        }
    }

    /*****************************************************
     * Internal Functions
     ***************************************/
    private void ActivateCamera() {
        if (m_camera != null) {
            m_camera = null;
        }
        try {
            m_camera = Camera.open();
        } catch (Exception e) {
            m_status.setText("The camera could not be accessed");
        }
        if (m_decode != null) {
            m_decode = null;
        }
        m_decode = new Decode(this);
        if (m_preview != null) {
            m_preview = null;
        }
        try {
            Camera.Parameters params;
            params = m_camera.getParameters();
            params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
            m_camera.setParameters(params);

        } catch (Exception e) {
        }
        m_preview = new CameraPreview(this, m_camera);
        m_cameraFrame.addView(m_preview);
    }
}

And when it launches it works fine. The camera comes up, it autofocuses in a loop until the can decode the image. Then passes it back by calling a function which spawns a "runnable".

Then I created an activity to "put in front" of this one. I called int Main2Activity (as auto generated by Android Studio), and I launch "MainActivity" it with an intent.

   package com.digitalcheck.barcodedecode;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class Main2Activity extends AppCompatActivity {
    private final int CAMERA_REQUEST = 1;
    private String m_data;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
    }
    public void OpenCamera(View view) {

        Intent intent = new Intent(this,MainActivity.class);
        startActivityForResult(intent,CAMERA_REQUEST);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode,resultCode,data);
        if (requestCode == CAMERA_REQUEST) {
            if (resultCode == RESULT_OK) {
                m_data = data.getStringExtra(MainActivity.EXTRA_REPLY);
                if (m_data.length() > 0) {

                }
            }
        }
    }


}

Obviously I also updated the manifest by swapping the two activities:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.digitalcheck.barcodedecode">

    <uses-permission android:name="android.permission.CAMERA" />

    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-feature
        android:name="android.hardware.camera"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:screenOrientation="landscape"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"></activity>
        <activity
            android:name=".Main2Activity"
            android:label="Barcode Decode"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Both activities are included in my single app. All I do is change the manifest to launch "Main2Activity" instead of "MainActivity" and the autofocus stops working.

It's an easy and obvious test. When launching MainActivity, I can hold some paper in front of the camera, and watch it autofocus. When launching Main2Activity, then press the button to bring up MainActivity, it shows the camera, but the autofocus never occurs, and the autofocus callback never happens.

And yet the camera is the same class. So the issue must lie elsewhere.

Any advice would be appreciated.

-Scotty EDIT: As I cannot attach a zip file of code, but I can embed an image. A picture is worth 1000 words (or a word is one milli-picture)

Classes and Manifest drawing

I hope this helps clear up any vagaries in my description.

EDIT 2:

In the hopes of obtaining help from a more experienced Android developer than myself, I created a GitHub account, and put a zip of the example app on it.

https://github.com/scotty2541/AndroidCamera

Detailing the issue, simply changing the manifest to launch the MainActivity vs using Main2Activity shows the AutoFocus working with the former but not the latter.

-Scotty

SpacemanScott
  • 953
  • 9
  • 21

1 Answers1

0

Update: I found the solution.

The call to Camera.autoFocus(callBack) actually needed to be inside the

public class CameraPreview extends SurfaceView

object.

Specifically, the original project was invoking the auto focus in the MainActivity, during the post resume:

    @Override
public void onPostResume() {
    super.onPostResume();
    if (m_camera == null) {
        ActivateCamera();
    }
    if (m_camera != null) {
        m_camera.autoFocus(m_decode);
    }
}

However, it appears the surface is coupled to the auto focus, and was not completely instantiated in the case where it failed. It DOES appear the surface is fully instantiated before onPostResume() is called when the activity is the primary LAUNCHED activity.

The specific solution in my problem was to add the callback after the surface started the preview. I placed it in the following call inside the CameraPreview class that extends the SurfaceView

    public void surfaceChanged(SurfaceHolder holder,  int format, int w, int h) {
    if (m_holder.getSurface() == null) {
        return;
    }
    m_holder = holder;
    try {
        Camera.Parameters params;
        m_camera.stopPreview();
        params = m_camera.getParameters();
        params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
        m_camera.setParameters(params);
        m_camera.setPreviewDisplay(holder);
        m_camera.startPreview();
        m_camera.autoFocus(m_decode);

    } catch (Exception e) {
        Log.d("CSW","Exception");
    }

I have also updated the project on GitHub, should anyone wish to review it, learned from it, or offer any constructive advice about it.

Hopefully others will find this useful.

-Scotty

SpacemanScott
  • 953
  • 9
  • 21