We're building a mobile app for Android using CodeName One. As part of our requirement, we already have an HTML5 page that contains a video element to use a device's camera for capturing photos. We need to call this HTML5 page from within our CodeName One app. But this code is not working as per the expectations, however the html file URL is working perfectly fine when called directly from chrome browser. When viewed from the chrome browser I get the prompt for asking camera permission where as in CodeName One app I don't see the permission prompt.
I saw a similar question posted recently html video control is not working in codenameone app for android
I've tried the recommendations given there, but it hasn't worked for me. Maybe something wrong that I'm doing here?
In my CodeName one class, I have the following code. The code basically creates a new BrowserComponent and sets the camera1.html URL in this component. The camera1.html contains HTML5 code that displays the video element for taking pictures. `
private Form current;
private Resources theme;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
}
public void start(){
if(current != null) {
current.show();
return;
}
Form f = new Form("Camera", new BorderLayout());
try {
CN1NativeCameraInterface cnci = NativeLookup.create(CN1NativeCameraInterface.class);
if (cnci != null && cnci.isSupported()) {
boolean cameraPerm = cnci.promptCameraPermission();
if (cameraPerm) {
f.setTitle("Camera Permission true");
}
}
BrowserComponent browser = new BrowserComponent();
browser.setURL("https://8c10ec77.ngrok.io/app/camera1.html");
f.add(BorderLayout.CENTER, browser);
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
f.show();
}
}
public void stop(){
current = Display.getInstance().getCurrent();
}
public void destroy(){
}
` I have defined a native interface that looks like this
public interface CN1NativeCameraInterface extends NativeInterface {
public boolean promptCameraPermission();
}
Generated the native access code for this interface in CodeName One and the implementation for Android is as follows
import com.codename1.impl.android.AndroidImplementation;
import android.Manifest;
public class CN1NativeCameraInterfaceImpl {
public boolean promptCameraPermission() {
return AndroidImplementation.checkForPermission(Manifest.permission.CAMERA, "This is required to take a picture");
}
public boolean isSupported() {
return true;
}
}
When I run this on an Android device, I can see the HTML page getting loaded, but the video element doesn't load, All I see is the placeholder for the video element. The app does not ask for camera permission either, something that was suggested in the other related question.
What am I doing wrong here?
Update 1:
I see the following errors in the log obtained from the device using the adb shell "logcat --pid=$(pidof -s com.codename1)" command:
Error1 -
09-06 11:48:42.530 10172 10202 I WebViewFactory: Loading com.android.chrome version 69.0.3497.76 (code 349707652) 09-06 11:48:42.595 10172 10202 I zygote64: Rejecting re-init on previously-failed class java.lang.Class: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/webkit/TracingController; 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader) (Class.java:-2) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader) (Class.java:453) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class android.webkit.WebViewFactory.getWebViewProviderClass(java.lang.ClassLoader) (WebViewFactory.java:151) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class android.webkit.WebViewFactory.getProviderClass() (WebViewFactory.java:472) 09-06 11:48:42.595 10172 10202 I zygote64: at android.webkit.WebViewFactoryProvider android.webkit.WebViewFactory.getProvider() (WebViewFactory.java:212) 09-06 11:48:42.595 10172 10202 I zygote64: at android.webkit.CookieManager android.webkit.CookieManager.getInstance() (CookieManager.java:39) 09-06 11:48:42.595 10172 10202 I zygote64: at android.webkit.CookieManager com.codename1.impl.android.AndroidImplementation.getCookieManager() (AndroidImplementation.java:4511) 09-06 11:48:42.595 10172 10202 I zygote64: at java.util.Vector com.codename1.impl.android.AndroidImplementation.getCookiesForURL(java.lang.String) (AndroidImplementation.java:4528) 09-06 11:48:42.595 10172 10202 I zygote64: at void com.codename1.io.ConnectionRequest.performOperation() (ConnectionRequest.java:633) 09-06 11:48:42.595 10172 10202 I zygote64: at void com.codename1.io.NetworkManager$NetworkThread.run() (NetworkManager.java:282) 09-06 11:48:42.595 10172 10202 I zygote64: at void com.codename1.impl.CodenameOneThread$1.run() (CodenameOneThread.java:60) 09-06 11:48:42.595 10172 10202 I zygote64: at void java.lang.Thread.run() (Thread.java:764) 09-06 11:48:42.595 10172 10202 I zygote64: Caused by: java.lang.ClassNotFoundException: Didn't find class "android.webkit.TracingController" on path: DexPathList[[zip file "/data/app/com.android.chrome-fq476TNc_btk-Vb0sBbuyw==/base.apk"],nativeLibraryDirectories=[/data/app/com.android.chrome-fq476TNc_btk-Vb0sBbuyw==/lib/arm64, /data/app/com.android.chrome-fq476TNc_btk-Vb0sBbuyw==/base.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]] 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:93) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader) (Class.java:-2) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader) (Class.java:453) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class android.webkit.WebViewFactory.getWebViewProviderClass(java.lang.ClassLoader) (WebViewFactory.java:151) 09-06 11:48:42.595 10172 10202 I zygote64: at java.lang.Class android.webkit.WebViewFactory.getProviderClass() (WebViewFactory.java:472) 09-06 11:48:42.595 10172 10202 I zygote64: at android.webkit.WebViewFactoryProvider android.webkit.WebViewFactory.getProvider() (WebViewFactory.java:212) 09-06 11:48:42.595 10172 10202 I zygote64: at android.webkit.CookieManager android.webkit.CookieManager.getInstance() (CookieManager.java:39) 09-06 11:48:42.595 10172 10202 I zygote64: at android.webkit.CookieManager com.codename1.impl.android.AndroidImplementation.getCookieManager() (AndroidImplementation.java:4511) 09-06 11:48:42.595 10172 10202 I zygote64: at java.util.Vector com.codename1.impl.android.AndroidImplementation.getCookiesForURL(java.lang.String) (AndroidImplementation.java:4528) 09-06 11:48:42.595 10172 10202 I zygote64: at void com.codename1.io.ConnectionRequest.performOperation() (ConnectionRequest.java:633)
Error 2 -
09-06 11:48:44.506 10172 10266 W VideoCapabilities: Unrecognized profile 2130706433 for video/avc 09-06 11:48:44.506 10172 10266 W VideoCapabilities: Unrecognized profile 2130706434 for video/avc 09-06 11:48:44.536 10172 10266 W VideoCapabilities: Unrecognized profile 2130706433 for video/avc 09-06 11:48:44.536 10172 10266 W VideoCapabilities: Unrecognized profile 2130706434 for video/avc 09-06 11:48:44.542 10172 10266 W VideoCapabilities: Unrecognized profile 2130706433 for video/avc 09-06 11:48:44.542 10172 10266 W VideoCapabilities: Unrecognized profile 2130706434 for video/avc 09-06 11:48:44.551 10172 10266 W VideoCapabilities: Unrecognized profile/level 0/3 for video/mpeg2 09-06 11:48:44.555 10172 10266 W VideoCapabilities: Unrecognized profile/level 0/3 for video/mpeg2 09-06 11:48:44.564 10172 10266 W VideoCapabilities: Unsupported mime video/x-ms-wmv 09-06 11:48:44.568 10172 10266 W VideoCapabilities: Unsupported mime video/x-ms-wmv 09-06 11:48:44.574 10172 10266 W VideoCapabilities: Unsupported mime video/divx 09-06 11:48:44.577 10172 10177 I zygote64: Do partial code cache collection, code=55KB, data=56KB 09-06 11:48:44.580 10172 10266 W VideoCapabilities: Unsupported mime video/divx311 09-06 11:48:44.584 10172 10266 W VideoCapabilities: Unsupported mime video/divx4 09-06 11:48:44.585 10172 10177 I zygote64: After code cache collection, code=55KB, data=56KB 09-06 11:48:44.585 10172 10177 I zygote64: Increasing code cache capacity to 256KB 09-06 11:48:44.605 10172 10266 W VideoCapabilities: Unsupported mime video/mp4v-esdp 09-06 11:48:44.633 10172 10266 I VideoCapabilities: Unsupported profile 4 for video/mp4v-es 09-06 11:48:44.867 10172 10282 W cr_CrashFileManager: /data/user/0/com.codename1/cache/WebView/Crash Reports does not exist or is not a directory
Error 3 -
09-06 11:48:47.715 10172 10253 I CameraManagerGlobal: Connecting to camera service 09-06 11:48:47.799 10172 10172 D Codename One: onPermissionRequest 09-06 11:48:50.952 10172 10172 E AndroidImplementation: null 09-06 11:48:50.952 10172 10172 E AndroidImplementation: java.io.FileNotFoundException: /data/user/0/com.codename1/files/CN1$AndroidServiceProperties (No such file or directory) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at java.io.FileInputStream.open0(Native Method) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at java.io.FileInputStream.open(FileInputStream.java:200) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at java.io.FileInputStream.(FileInputStream.java:150) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.ContextImpl.openFileInput(ContextImpl.java:515) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.content.ContextWrapper.openFileInput(ContextWrapper.java:190) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at com.codename1.impl.android.AndroidImplementation.getServiceProperties(AndroidImplementation.java:6081) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at com.codename1.impl.android.AndroidImplementation.writeServiceProperties(AndroidImplementation.java:6107) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at com.codename1.CameraDemoStub.onStop(CameraDemoStub.java:166) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1486) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.Activity.performStop(Activity.java:7178) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4274) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4333) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.ActivityThread.-wrap24(Unknown Source:0) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1722) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.os.Handler.dispatchMessage(Handler.java:105) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.os.Looper.loop(Looper.java:164) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at android.app.ActivityThread.main(ActivityThread.java:6798) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at java.lang.reflect.Method.invoke(Native Method) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 09-06 11:48:50.952 10172 10172 E AndroidImplementation: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Error 4 -
09-06 11:48:51.005 10172 10172 D CameraDemo: [main] 0:0:0,3 - [LOG] The following error occured: NotAllowedError: Permission denied On line 55 of https://8319bf05.ngrok.io/ventasys/venta/code/camera1.html
This above error (Error 4) is coming out of the .html that is called from CodeName One. Here's the code for HTML:
<!DOCTYPE HTML>
<html>
<head>
<script>
function copyPhoto() {
var imgName;
imgName = getParamValues("imgNm");
window.opener.document.getElementById(imgName).src = document.getElementById("theimage1").src;
window.close();
}
function getParamValues(paramNm) {
var qstring = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < qstring.length; i++) {
var urlparam = qstring[i].split('=');
if (urlparam[0] == paramNm) {
return urlparam[1];
}
}
}
function snapshot() {
var canvas = document.getElementById("thecanvas");
var ctx = canvas.getContext("2d");
//var constraints = { video: { facingMode: "user" }, audio: false };
//var constraints = {
// video: {
// width: { min: 640, ideal: 1920 },
// height: { min: 400, ideal: 1080 },
// aspectRatio: { ideal: 1.7777777778 },
// facingMode: "environment"
// },
// audio: false
//};
var constraints = {
video: { facingMode: "user" },
audio: false
};
try {
navigator.mediaDevices.getUserMedia(constraints).then(
// successCallback
function (localMediaStream) {
document.getElementById("mylog").innerHTML = document.getElementById("mylog").innerHTML + '1.3 ';
video = document.querySelector('video');
video.srcObject = localMediaStream;
})
.catch(function (err) {
console.log("The following error occured: " + err);
document.getElementById("mylog").innerHTML = document.getElementById("mylog").innerHTML + ' <br> 5 Err' + err;
});
}
catch (e)
{
document.getElementById("mylog").innerHTML = document.getElementById("mylog").innerHTML + ' 6 catch ##** ' + e;
}
document.getElementById("mylog").innerHTML = document.getElementById("mylog").innerHTML + '10 ';
canvas.width = 640;
canvas.height = video.videoHeight / (video.videoWidth / canvas.width);
// Draws current image from the video element into the canvas
//ctx.drawImage(video, 0,0, video.videoWidth, video.videoHeight);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
var canvas = document.getElementById("thecanvas");
var dataUrl = canvas.toDataURL('image/jpeg', 0.6);
}
</script>
</head>
<body onload="snapshot();">
<div id='mylog'></div>
<p><video id="video" controls autoplay></video></p>
<p><button onclick="snapshot();">Take Snapshot</button></p>
<div id='item' hidden>
<p><canvas id="thecanvas"></canvas></p>
</div>
<p><image id="theimage1"></image></p>
<p><button onclick="copyPhoto()">Done</button></p>
<p><button onclick="copyPhoto()">Reload</button></p>
</body>
</html>