tgkprog
tgkprog

Reputation: 4598

Android using a runtime strategy to support different API level?

I have a small functionality. Switching on the torch and keeping it on, till the user switches it off from my app or my app exits. Using :

            params = camera.getParameters();
            if (params.getFlashMode().equals(Parameters.FLASH_MODE_TORCH)) {
                isFlashOn = true;
                return;
            }
            params.setFlashMode(Parameters.FLASH_MODE_TORCH);
            camera.setParameters(params);
            camera.startPreview();

And to switch off :

            if (params.getFlashMode().equals(Parameters.FLASH_MODE_OFF)) {
                isFlashOn = false;
                return;
            }
            params.setFlashMode(Parameters.FLASH_MODE_OFF);
            camera.setParameters(params);
            camera.stopPreview();

But I notice that this is not very robust. Works fine the first time, but after that (especially on my API levle 22 phone) might not work. I was thinking of testing with the android.hardware.camera2 as suggested here Plan to use if (android.os.Build.VERSION.SDK_INT >20) and a strategy (a base interface implemented by two classes, one using old API and one the new camera2 API.

Is this safe on all devices? I saw that we can do it for android classes, but is it okay for our own classes too? Or are there devices which scan all our code and reject it if it has code that refers to API it does not know about?

I do not want to make two APKs as its a small functionality.

I make sure flash is available like this , not tested on all devices but in an emulator by Genymotion app did not crash.

public boolean torchInit() {
    try {
        PackageManager pm = app.getPackageManager();
        // First check if device supports flashlight
        boolean hasFlash = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
        if (!hasFlash) {
            Toast.makeText(app, "Flash not found or can\'t get hold of it. No torch", Toast.LENGTH_LONG).show();
            return false;
        }
        camera = Camera.open();
        Camera.Parameters params = camera.getParameters();
        Log.i(LogId, "camera params flash: " + params.getFlashMode());
        return true;
    } catch (Throwable e) {
        Log.e(LogId, "cameraFlashSetup " + e, e);
        Toast.makeText(app, "Flash error, no torch. Description : " + e, Toast.LENGTH_LONG).show();
        camera = null;
    }
    return false;
}

The flash interface to change between the two classes is :

public abstract class TorchInterface  {

protected AppCompatActivity app;

public void init(AppCompatActivity ap){
    app = ap;
}

public abstract boolean torchInit();

public boolean torchReInit(){
    return torchInit();//if re init is not different than init
}

public abstract boolean torchOn();

public abstract boolean torchOff();

}

Update: new code worked but only if I:

 mBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

Instead of:

mBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_MANUAL);

But then switches on flash as soon as init the app. I was going to chuck it, then realised on my Camera2Impl I can :

public boolean torchInit() {
    //do nothing on usual init that app calls on create
    return true;
}

And instead do the init on torch on (flash on):

public boolean torchOn() {
        //if not in it, try first 3 times
        if(mBuilder == null || mSession == null){
            if(firstFewTimesTorchOn >  0){
                firstFewTimesTorchOn--;
                torchInit2();
               try{
                    Thread.sleep(150);
                }catch(Exception e){}
                if(mBuilder == null || mSession == null) {
                    return false;
                }
            }
        }
        try {
            mBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);//and etc

Upvotes: 4

Views: 144

Answers (2)

Mukesh
Mukesh

Reputation: 258

Is this safe on all devices?

Why dont't you put one check whether flash is available or not.

context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);

which will return true if a flash is available, false if not. You can write your further code in true block.

Upvotes: 2

Vasiliy
Vasiliy

Reputation: 16228

Android devices do not "scan" code - compiler does. Therefore, I don't see any issue with your idea. On contrary - using Strategy pattern is way better then if-else all over the code.

Something along these lines should work:

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
    mFlashlightStrategy = new PostLollipopStrategy();
} else {
    mFlashlightStrategy = new PreLollipopStrategy();
}

Upvotes: 2

Related Questions