I've been struggling to implement a camera function into my app in a way that doesn't generate the below error:
E/ContextImpl: Tried to access visual service WindowManager from a non-visual Context:com.camtest.App@385f002 Visual services, such as WindowManager, WallpaperService or LayoutInflater should be accessed from Activity or other visual Context. Use an Activity or a Context created with Context#createWindowContext(int, Bundle), which are adjusted to the configuration and visual bounds of an area on screen. java.lang.IllegalAccessException: Tried to access visual service WindowManager from a non-visual Context:com.camtest.App@385f002
That error is triggered by this line:
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
I looked at implementing createWindowContext
as the error suggests, but some of the target devices are older and not eligible for upgrade to Android 11, thus createWindowContext
is not an option.
The first time around, I followed one of the CodeLabs for implementing CameraX. The camera behaved as expected, but triggered the exception. So I found a different example of implementing CameraX, but I get the same IllegalAccessException
exception.
Any suggestions?
package com.camtest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CamTest extends AppCompatActivity {
private Executor executor = Executors.newSingleThreadExecutor();
private int REQUEST_CODE_PERMISSIONS = 9001;
private final String[] REQUIRED_PERMISSIONS = new String[]{"android.permission.CAMERA", "android.permission.WRITE_EXTERNAL_STORAGE"};
PreviewView mPreviewView;
ImageView captureImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_test);
mPreviewView = findViewById(R.id.camera);//was previewView
captureImage = findViewById(R.id.captureImg);
if(allPermissionsGranted()){
startCamera(); //start camera if permission has been granted by user
} else{
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
}
}
private void startCamera() {
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); //This line triggers `E/ContextImpl: Tried to access visual service WindowManager from a non-visual Context`
cameraProviderFuture.addListener(new Runnable() {
@Override
public void run() {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// No errors need to be handled for this Future.
// This should never be reached.
}
}
}, ContextCompat.getMainExecutor(this));
}
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder().build();
ImageCapture imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
Camera camera = cameraProvider.bindToLifecycle(
((LifecycleOwner) this),
cameraSelector,
preview,
imageCapture);
preview.setSurfaceProvider(
mPreviewView.getSurfaceProvider());
captureImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
File file = new File(getBatchDirectoryName(), mDateFormat.format(new Date())+ ".jpg");
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
imageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback () {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
new Handler().post(new Runnable() {
@Override
public void run() {
Toast.makeText(CamTest.this, "Image Saved successfully", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onError(@NonNull ImageCaptureException error) {
error.printStackTrace();
}
});
}
});
}
public String getBatchDirectoryName() {
String app_folder_path = "";
app_folder_path = Environment.getExternalStorageDirectory().toString() + "/images";
File dir = new File(app_folder_path);
if (!dir.exists() && !dir.mkdirs()) {
}
return app_folder_path;
}
private boolean allPermissionsGranted(){
for(String permission : REQUIRED_PERMISSIONS){
if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
return false;
}
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == REQUEST_CODE_PERMISSIONS){
if(allPermissionsGranted()){
startCamera();
} else{
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
this.finish();
}
}
}
}
And this activity is started by the below code within onCreate of MainActivity:
Button button_test = findViewById(R.id.button_test);
button_test.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, CamTest.class);
startActivityForResult(intent,0);
});
EDIT: full stack trace-
E/ContextImpl: Tried to access visual service WindowManager from a non-visual Context:com.camtest.App@dd90e6b Visual services, such as WindowManager, WallpaperService or LayoutInflater should be accessed from Activity or other visual Context. Use an Activity or a Context created with Context#createWindowContext(int, Bundle), which are adjusted to the configuration and visual bounds of an area on screen.
java.lang.IllegalAccessException: Tried to access visual service WindowManager from a non-visual Context:com.camtest.App@dd90e6b
at android.app.ContextImpl.getSystemService(ContextImpl.java:1914)
at android.content.ContextWrapper.getSystemService(ContextWrapper.java:803)
at androidx.camera.camera2.internal.Camera2UseCaseConfigFactory.<init>(Camera2UseCaseConfigFactory.java:50)
at androidx.camera.camera2.Camera2Config.lambda$defaultConfig$1(Camera2Config.java:60)
at androidx.camera.camera2.-$$Lambda$Camera2Config$g_hY10kZhqC56um0PalOLTzuFlU.newInstance(Unknown Source:0)
at androidx.camera.core.CameraX.lambda$initAndRetryRecursively$9$CameraX(CameraX.java:575)
at androidx.camera.core.-$$Lambda$CameraX$u-Xx2b6YXY5GXNXRh-mDiDnHdpQ.run(Unknown Source:10)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
EDIT #2: In order to reproduce this error, the StrictMode for VmPolicy must be enabled. The below code is added to MainActivity.onCreate:
if( BuildConfig.BUILD_TYPE.contentEquals( "debug" ) ){
/*StrictMode.setThreadPolicy( new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());*/
StrictMode.setVmPolicy( new StrictMode.VmPolicy.Builder()
.detectAll()//.detectNonSdkApiUsage()
.penaltyLog()
.build());
}
EDIT #3: Updating from CameraX version 1.0.0-beta12 to 1.0.0-rc1 (current version as of today) had no effect
Pass the Context
from the Activity
rather than the Application
.
The stack trace indicates you're passing an instance of com.camtest.App
. Since you're just passing it from your Activity.this
, I imagine the library you're using is calling Context.getApplicationContext()
incorrectly. You'll need to chase this up with the library maintainers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With