getHolder() public method

public getHolder ( ) : global::android.view.SurfaceHolder
return global::android.view.SurfaceHolder
        public static File InternalTakePicture(int num = 0)
        {
            var DIRECTORY_DCIM = global::android.os.Environment.DIRECTORY_DCIM;


            var path = global::android.os.Environment.getExternalStoragePublicDirectory(DIRECTORY_DCIM).getAbsolutePath();
            path += "/Camera";


            //var SAVE_PATH = android.os.Environment.getExternalStoragePublicDirectory(
            //    android.os.Environment.DIRECTORY_PICTURES
            //) + "/";

            var n = DateTime.Now;

            var f = new File(path + "/shot" + n.Ticks + ".jpg");

            //I/System.Console(31472): enter TakePicture
            //W/CameraService(  128): CameraService::connect X (pid 31472) rejected (existing client).
            //I/System.Console(31472): error takePicture { Message = Fail to connect to camera service, StackTrace = java.lang.RuntimeException: Fail to connect to camera service
            //I/System.Console(31472):        at android.hardware.Camera.native_setup(Native Method)
            //I/System.Console(31472):        at android.hardware.Camera.<init>(Camera.java:340)
            //I/System.Console(31472):        at android.hardware.Camera.open(Camera.java:302)
            var camera = android.hardware.Camera.open(num);

            //            W/CameraService(  128): CameraService::connect X (pid 2499) rejected (existing client).
            //D/dalvikvm( 2499): GC_CONCURRENT freed 873K, 12% free 7525K/8544K, paused 4ms+4ms, total 59ms
            //D/dalvikvm( 2499): WAIT_FOR_CONCURRENT_GC blocked 14ms
            //I/System.Console( 2499): error takePicture { Message = Fail to connect to camera service, StackTrace = java.lang.RuntimeException: Fail to connect to camera service
            //I/System.Console( 2499):        at android.hardware.Camera.native_setup(Native Method)
            //I/System.Console( 2499):        at android.hardware.Camera.<init>(Camera.java:340)
            //I/System.Console( 2499):        at android.hardware.Camera.open(Camera.java:302)
            //I/System.Console( 2499):        at CameraExperiment.foo.InternalTakePicture(foo.java:65)

            var p = camera.getParameters();

            p.setRotation(0);

            //camera.stopFaceDetection();

            var s = p.getSupportedPictureSizes();

            var min = default(android.hardware.Camera.Size);

            for (int i = 0; i < s.size(); i++)
            {
                var size = (android.hardware.Camera.Size)s.get(i);

                //                I/System.Console( 6058): before takePicture { f = /mnt/sdcard/Pictures/shot.jpg }
                //I/System.Console( 6058): { size = android.hardware.Camera$Size@4fde180 }

                System.Console.WriteLine(new { size.width, size.height });

                if (min == null)
                    min = size;
                else if (min.width > size.width)
                    min = size;


            }

            System.Console.WriteLine("before setPictureSize ");
            p.setPictureSize(min.width, min.height);

            //E/CameraHardwareSec(   84): android::status_t android::CameraHardwareSec::setSceneModeParameter(const android::CameraParameters&): unmatched focus_mode(continuous-picture)
            //E/CameraHardwareSec(   84): virtual android::status_t android::CameraHardwareSec::setParameters(const android::CameraParameters&): Failed to setting scene mode


            var focusModes = p.getSupportedFocusModes();
            var NextFocus = android.hardware.Camera.Parameters.FOCUS_MODE_FIXED;

            for (int i = 0; i < focusModes.size(); i++)
            {
                var focusMode = (string)focusModes.get(i);

                if (focusMode == android.hardware.Camera.Parameters.FOCUS_MODE_INFINITY)
                    NextFocus = android.hardware.Camera.Parameters.FOCUS_MODE_INFINITY;

                System.Console.WriteLine(new { focusMode });
            }

            //            I/System.Console(31232): before setPictureSize
            //I/System.Console(31232): { focusMode = fixed }
            //I/System.Console(31232): before setFocusMode
            //E/NvOmxCameraSettingsParser(  128): Failed substring capabilities check, unsupported parameter: 'infinity', original: fixed
            //E/NvOmxCameraSettingsParser(  128): extractChanges: Invalid parameter!
            //E/NvOmxCamera(  128): setParameters: Invalid parameters
            //I/System.Console(31232): error takePicture { Message = setParameters failed, StackTrace = java.lang.RuntimeException: setParameters failed

            // { focusMode = auto }
            // { focusMode = infinity }
            // { focusMode = macro }
            // before setFocusMode
            //9): android::status_t android::CameraHardwareSec::setSceneModeParameter(const android::CameraParameters&): unmatched focus_mode(fixed)
            //9): virtual android::status_t android::CameraHardwareSec::setParameters(const android::CameraParameters&): Failed to setting scene mode
            // error takePicture { Message = setParameters failed, StackTrace = java.lang.RuntimeException: setParameters failed
            //        at android.hardware.Camera.native_setParameters(Native Method)
            //        at android.hardware.Camera.setParameters(Camera.java:950)
            //        at CameraExperiment.foo.InternalTakePicture(foo.java:105)

            //            E/SecCamera(   84): ERR(int android::fimc_v4l2_s_ctrl(int, unsigned int, unsigned int)):VIDIOC_S_CTRL(id = 0x800005b (91), value = 0) failed ret = -1
            //E/SecCamera(   84): ERR(int android::SecCamera::setFaceDetect(int)):Fail on V4L2_CID_CAMERA_FACE_DETECTION
            //E/SecCamera(   84): ERR(int android::fimc_v4l2_s_ctrl(int, unsigned int, unsigned int)):VIDIOC_S_CTRL(id = 0x8000063 (99), value = 6) failed ret = -1
            //E/SecCamera(   84): ERR(int android::SecCamera::setFocusMode(int)):Fail on V4L2_CID_CAMERA_FOCUS_MODE
            //E/CameraHardwareSec(   84): android::status_t android::CameraHardwareSec::setSceneModeParameter(const android::CameraParameters&): mSecCamera->setFocusMode(6) fail
            //E/CameraHardwareSec(   84): virtual android::status_t android::CameraHardwareSec::setParameters(const android::CameraParameters&): Failed to setting scene mode
            //E/SecCamera(   84): ERR(int android::fimc_v4l2_s_ctrl(int, unsigned int, unsigned int)):VIDIOC_S_CTRL(id = 0x800006c (108), value = 1) failed ret = -1
            //E/SecCamera(   84): ERR(int android::SecCamera::setBatchReflection()):Fail on V4L2_CID_CAMERA_BATCH_REFLECTION
            //E/CameraHardwareSec(   84): ERR(virtual android::status_t android::CameraHardwareSec::setParameters(const android::CameraParameters&)):Fail on mSecCamera->setBatchCmd


            System.Console.WriteLine("before setFocusMode " + new { NextFocus });
            //p.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_INFINITY);
            p.setFocusMode(NextFocus);


            //            E/SecCamera(   84): ERR(int android::fimc_poll(pollfd*)):No data in 10 secs..
            //I/ShotSingle(   84): CAMERA_MSG_COMPRESSED_IMAGE

            camera.setParameters(p);

            // http://stackoverflow.com/questions/9744790/android-possible-to-camera-capture-without-a-preview
            var b = new EventWaitHandle(false, EventResetMode.ManualReset);
            System.Console.WriteLine("before startPreview ");


            Action done = delegate { };

            try
            {
                // #5 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

                (ScriptCoreLib.Android.ThreadLocalContextReference.CurrentContext as Activity).With(
                aa =>
                {
                    aa.runOnUiThread(
                       new f
                       {
                           y = delegate
                           {
                               try
                               {
                                   // D/Camera  ( 2464): app passed NULL surface

                                   System.Console.WriteLine("before getHolder ");

                                   //  the nexus 7 and droid x both don't support the passing of a dummy surfaceview to a camera object. Your response that all camera things must created in the activity is false. I was able to instantiate a camera within a thread by passing it a view just fine. 

                                   // here, the unused surface view and holder
                                   var dummy = new SurfaceView(ScriptCoreLib.Android.ThreadLocalContextReference.CurrentContext);

                                   // missing for android 2.2
                                   //dummy.setScaleX(0f);
                                   //dummy.setScaleY(0f);

                                   var h = dummy.getHolder();

                                   // http://developer.android.com/reference/android/view/SurfaceHolder.html#SURFACE_TYPE_PUSH_BUFFERS
                                   var SURFACE_TYPE_PUSH_BUFFERS = 0x00000003;
                                   h.setType(SURFACE_TYPE_PUSH_BUFFERS);

                                   h.addCallback(
                                       new XSurfaceHolder_Callback
                                       {
                                           yield_surfaceCreated = delegate
                                           {
                                               System.Console.WriteLine("at yield_surfaceCreated ");

                                               try
                                               {

                                                   camera.setPreviewDisplay(h);
                                                   camera.startPreview();

                                                   System.Console.WriteLine("after startPreview ");

                                                   b.Set();
                                               }
                                               catch
                                               {
                                                   throw;
                                               }
                                           }
                                       }
                                   );

                                   //h.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);


                                   aa.addContentView(dummy, new android.widget.LinearLayout.LayoutParams(
                                     android.widget.LinearLayout.LayoutParams.WRAP_CONTENT,
                                     android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
                                     )
                                   );

                                   done = delegate
                                   {
                                       aa.runOnUiThread(
                                          new f
                                          {
                                              y = delegate
                                              {
                                                  // https://groups.google.com/forum/?fromgroups#!topic/android-developers/liph4z9LnFA

                                                  // how to Orphanize??
                                                  dummy.setVisibility(View.GONE);

                                              }
                                          }
                                      );

                                   };



                               }
                               catch
                               {
                                   throw;
                               }

                           }
                       }
                        );
                }
                );


            }
            catch
            {
                throw;
            }
            b.WaitOne();

            //camera.@lock();
            var a = new EventWaitHandle(false, EventResetMode.ManualReset);
            //var b = new EventWaitHandle(false, EventResetMode.ManualReset);

            //            E/SecCamera(   84): ERR(int android::fimc_v4l2_s_ctrl(int, unsigned int, unsigned int)):VIDIOC_S_CTRL(id = 0x800005d (93), value = 1) failed ret = -1
            //E/SecCamera(   84): ERR(int android::SecCamera::setAutofocus()):Fail on V4L2_CID_CAMERA_SET_AUTO_FOCUS
            //E/CameraHardwareSec(   84): ERR(int android::CameraHardwareSec::autoFocusThread()):Fail on mSecCamera->setAutofocus()

            //System.Console.WriteLine("before autoFocus " + new { f });

            ////

            //camera.autoFocus(
            //    new XAutoFocus
            //    {
            //        yield = delegate
            //        {
            //            System.Console.WriteLine("at autoFocus " + new { f });

            // https://github.com/mozilla-b2g/android-device-crespo/blob/master/libcamera/SecCamera.cpp



            //            E/SecCamera(   84): ERR(int android::fimc_poll(pollfd*)):No data in 10 secs..
            //I/ShotSingle(   84): CAMERA_MSG_COMPRESSED_IMAGE
            //D/dalvikvm( 6608): GC_FOR_MALLOC freed 607K, 58% free 2856K/6727K, external 2013K/2108K, paused 18ms
            //I/dalvikvm-heap( 6608): Grow heap (frag case) to 7.847MB for 614416-byte allocation
            //D/dalvikvm( 6608): GC_FOR_MALLOC freed 46K, 54% free 3410K/7367K, external 2013K/2108K, paused 13ms
            //I/System.Console( 6608): enter XCameraPictureCallback { Length = 0 }
            //I/System.Console( 6608): exit XCameraPictureCallback

            //for (int i = 0; i < 11; i++)
            //{
            //    System.Console.WriteLine("warming up camera machine... " + i);
            //    Thread.Sleep(1000);

            //}




            // http://stackoverflow.com/questions/15279911/using-camera-without-preview-or-surface-in-android
            // http://handycodeworks.com/?p=19
            // you are required to call startPreview() first before calling takePicture()
            System.Console.WriteLine("before takePicture " + new { f });

            camera.setErrorCallback(
                new XErrorCallback
                {
                    yield = (err, c) =>
                    {
                        System.Console.WriteLine(new { err });
                    }

                }
            );

            // preview ready?

            var at_setPreviewCallback = new EventWaitHandle(false, EventResetMode.ManualReset);

            System.Console.WriteLine("before setPreviewCallback ");
            // is this of any use?
            camera.setOneShotPreviewCallback(
                new XCameraPreviewCallback
                {
                    yield = delegate
                    {
                        at_setPreviewCallback.Set();
                    }
                }
            );

            at_setPreviewCallback.WaitOne();
            System.Console.WriteLine("after setPreviewCallback ");
            Thread.Sleep(150);


            camera.takePicture(
                null, null,
                new XCameraPictureCallback
                {
                    yield = (data, c) =>
                    {
                        System.Console.WriteLine("enter XCameraPictureCallback " + new { data.Length });

                        if (data.Length > 0)
                        {
                            var bmp = BitmapFactory.decodeByteArray(data, 0, data.Length);



                            File directory = new File(path);
                            directory.mkdirs();

                            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                            bmp.compress(Bitmap.CompressFormat.JPEG, 100, bytes);


                            try
                            {
                                f.createNewFile();

                                FileOutputStream fo = new FileOutputStream(f);
                                fo.write(bytes.toByteArray());
                            }
                            catch
                            {
                                throw;
                            }
                        }
                        System.Console.WriteLine("exit XCameraPictureCallback");

                        camera.release();

                        done();

                        //[javac] V:\src\CameraExperiment\ApplicationWebService___c__DisplayClass2.java:54: cannot find symbol
                        //[javac] symbol  : method Set()
                        //[javac] location: class ScriptCoreLibJava.BCLImplementation.System.Threading.__AutoResetEvent
                        //[javac]         this.a.Set();
                        //[javac]               ^

                        a.Set();
                    }
                }
            );




            //            I/System.Console( 6264): before takePicture { f = /mnt/sdcard/Pictures/shot.jpg }
            //I/System.Console( 6264): { width = 2560, height = 1920 }
            //I/System.Console( 6264): { width = 2560, height = 1536 }
            //I/System.Console( 6264): { width = 2048, height = 1536 }
            //I/System.Console( 6264): { width = 2048, height = 1232 }
            //I/System.Console( 6264): { width = 1600, height = 1200 }
            //I/System.Console( 6264): { width = 1600, height = 960 }
            //I/System.Console( 6264): { width = 800, height = 480 }
            //I/System.Console( 6264): { width = 640, height = 480 }
            //I/ShotSingle(   84): ShotSingle::takePicture start
            //I/ShotSingle(   84): ShotSingle::takePicture end
            //I/System.Console( 6264): after takePicture
            //        }
            //    }
            //);



            System.Console.WriteLine("will wait for takePicture to complete ... " + new { f });
            a.WaitOne();
            return f;
        }
        protected override void onCreate(Bundle savedInstanceState)
        {
            base.onCreate(savedInstanceState);

            Console.WriteLine("enter OVRVrCubeWorldSurfaceViewX onCreate");




            #region xCallback
            // X:\jsc.svn\examples\java\android\synergy\OVRVrCubeWorldSurfaceView\OVRVrCubeWorldSurfaceView\ApplicationActivity.cs
            var xCallback = new xCallback
            {
                onsurfaceCreated = holder =>
                {
                    //Console.WriteLine("enter onsurfaceCreated " + new { appThread });
                    if (appThread == 0)
                        return;

                    appThread.onSurfaceCreated(holder.getSurface());
                    mSurfaceHolder = holder;

                    //Console.WriteLine("exit onsurfaceCreated " + new { appThread });
                },

                onsurfaceChanged = (SurfaceHolder holder, int format, int width, int height) =>
                {
                    if (appThread == 0)
                        return;

                    appThread.onSurfaceChanged(holder.getSurface());
                    mSurfaceHolder = holder;
                },

                onsurfaceDestroyed = holder =>
                {
                    if (appThread == 0)
                        return;

                    appThread.onSurfaceDestroyed();
                    mSurfaceHolder = null;
                }
            };
            #endregion




            mView = new SurfaceView(this);
            this.setContentView(mView);

            var sw = Stopwatch.StartNew();

            #region mDraw
            var mDraw = new DrawOnTop(this)
            {
                // yes it appears top left.

                //text = "GearVR HUD"
                // (out) VrApi.vrapi_GetVersionString()
                text = () => sw.ElapsedMilliseconds + "ms " + GLES3JNILib.stringFromJNI()
            };

            //Task.Run(

            new Thread(
                delegate()
                {
                    // bg thread

                    while (true)
                    {
                        //Thread.Sleep(1000 / 15);
                        Thread.Sleep(1000 / 30);


                        mDraw.postInvalidate();
                    }
                }
            ).Start();
            #endregion

            #region ondispatchTouchEvent
            this.ondispatchTouchEvent = @event =>
            {
                if (appThread == 0)
                    return;

                int action = @event.getAction();
                float x = @event.getRawX();
                float y = @event.getRawY();
                //if (action == MotionEvent.ACTION_UP)
                {
                    var halfx = 2560 / 2;
                    var halfy = 1440 / 2;

                    mDraw.x = (int)(500 + halfx - x);
                    mDraw.y = (int)(600 + y - halfy);
                    //mDraw.text = () => sw.ElapsedMilliseconds + "ms \n" + new { x, y, action }.ToString();
                    //Console.WriteLine(" ::dispatchTouchEvent( " + action + ", " + x + ", " + y + " )");
                }
                appThread.onTouchEvent(action, x, y);

                // can we move hud around and record it to gif or mp4?
            };
            #endregion

            #region ondispatchKeyEvent
            this.ondispatchKeyEvent = @event =>
            {
                if (appThread == 0)
                    return false;

                int keyCode = @event.getKeyCode();
                int action = @event.getAction();
                if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP)
                {
                    return base.dispatchKeyEvent(@event);
                }
                if (action == KeyEvent.ACTION_UP)
                {
                    // keycode 4
                    mDraw.text = () => sw.ElapsedMilliseconds + "ms \n" + new { keyCode, action }.ToString();
                    //Log.v(TAG, "GLES3JNIActivity::dispatchKeyEvent( " + keyCode + ", " + action + " )");
                }
                appThread.onKeyEvent(keyCode, action);

                return true;
            };
            #endregion


            addContentView(mDraw, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

            mView.getHolder().addCallback(xCallback);

            getWindow().addFlags(WindowManager_LayoutParams.FLAG_KEEP_SCREEN_ON);

            appThread = com.oculus.gles3jni.GLES3JNILib.onCreate(this);

            Console.WriteLine("after OVRVrCubeWorldSurfaceViewX onCreate, attach the headset!");

        }
        // can we send a zip file?
        public static void Invoke(int index, Action<string> y, int frames = 4)
        {
            #region log
            var st = new Stopwatch();
            st.Start();
            Action<string> log =
                x =>
                {
                    var z = (st.Elapsed + " " + x);

                    Console.WriteLine(z);
                    y(z);
                };
            #endregion

            log("getting ready...");

            var DIRECTORY_DCIM = global::android.os.Environment.DIRECTORY_DCIM;

            var path = global::android.os.Environment.getExternalStoragePublicDirectory(DIRECTORY_DCIM).getAbsolutePath();
            path += "/Camera";

            //var n = DateTime.Now;
            //var f = new java.io.File(path + "/shot" + n.Ticks + ".jpg");

            var camera = default(android.hardware.Camera);
            var surface = default(SurfaceView);

            try
            {
                // PreviewCallbackWithBuffer { cc = 0, Length = 1048576 }
                // W/CameraService(   84): CameraService::connect X (pid 2117) rejected (existing client).

                log("android.hardware.Camera.open... " + new { index });

                camera = android.hardware.Camera.open(index);

                log("android.hardware.Camera.open... done ");

                var PreviewFormat = ImageFormat.UNKNOWN;

                #region setParameters
                var p = camera.getParameters();

                // The size of the buffer must match the values described above.
                // Gets the supported preview formats. NV21 is always supported.

                // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFormats()

                #region SupportedPictureFormat

                foreach (int SupportedPictureFormat in p.getSupportedPictureFormats().AsEnumerable())
                {
                    Console.WriteLine(new { SupportedPictureFormat });
                }

                //p.getSupportedPictureFormats().With(
                //     pformats =>
                //     {
                //         for (int i = 0; i < pformats.size(); i++)
                //         {
                //             var SupportedPictureFormat = (int)pformats.get(i);

                //             Console.WriteLine(new { SupportedPictureFormat });
                //         }
                //     }
                // );
                #endregion



                #region SupportedPreviewFormat

                foreach (int SupportedPreviewFormat in p.getSupportedPreviewFormats().AsEnumerable())
                {

                    if (SupportedPreviewFormat == ImageFormat.NV21)
                        PreviewFormat = SupportedPreviewFormat;
                    else if (PreviewFormat == ImageFormat.UNKNOWN)
                        PreviewFormat = SupportedPreviewFormat;

                    log("" + new { SupportedPreviewFormat });

                    Console.WriteLine(new { SupportedPreviewFormat });
                }
                //p.getSupportedPreviewFormats().With(
                //    pformats =>
                //    {
                //        for (int i = 0; i < pformats.size(); i++)
                //        {
                //            var SupportedPreviewFormat = (int)pformats.get(i);


                //            if (SupportedPreviewFormat == ImageFormat.NV21)
                //                PreviewFormat = SupportedPreviewFormat;
                //            else if (PreviewFormat == ImageFormat.UNKNOWN)
                //                PreviewFormat = SupportedPreviewFormat;

                //            log("" + new { SupportedPreviewFormat });
                //        }
                //    }
                //);
                #endregion


                //p.setPictureFormat(ImageFormat.YV12);

                p.setPreviewFormat(PreviewFormat);


                //D/DOMX    (  127): ERROR: failed check:(eError == OMX_ErrorNone) || (eError == OMX_ErrorNoMore) - returning error: 0x80001005 - Error returned from OMX API in ducati
                //E/CameraHAL(  127): Error while configuring rotation 0x80001005
                // http://questiontrack.com/galaxy-nexus-specificaly-camera-startpreview-failed-993603.html
                // http://stackoverflow.com/questions/16839869/orientation-error-causing-crash

                ////p.setRotation(0);

                #region getSupportedPreviewSizes
                //var s = p.getSupportedPreviewSizes();

                var min = default(android.hardware.Camera.Size);

                //for (int i = 0; i < s.size(); i++)
                foreach (android.hardware.Camera.Size size in p.getSupportedPreviewSizes().AsEnumerable())
                {
                    //var size = (android.hardware.Camera.Size)s.get(i);

                    //                I/System.Console( 6058): before takePicture { f = /mnt/sdcard/Pictures/shot.jpg }
                    //I/System.Console( 6058): { size = android.hardware.Camera$Size@4fde180 }

                    var SupportedPreviewSize = new { size.width, size.height };
                    log("" + new { SupportedPreviewSize });

                    if (min == null)
                        min = size;
                    else if (min.width > size.width)
                        min = size;

                }


                #endregion
                p.setPreviewSize(min.width, min.height);

                //For formats besides YV12, the size of the buffer is determined by multiplying the
                // preview image width, height, and bytes per pixel. The width and height can be read 
                // from getPreviewSize(). Bytes per pixel can be computed from getBitsPerPixel(int) / 8, 
                // using the image format from getPreviewFormat().

                //p.setPictureSize(min.width, min.height);


                // I/System.Console( 2860): { width = 640, height = 480, bytesperpixel = 0, buffersize = 0 }
                // the number of bits per pixel of the given format or -1 if the format doesn't exist or is not supported.
                //var bytesperpixel = (ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8);

                // http://stackoverflow.com/questions/13703596/mediacodec-and-camera-colorspaces-dont-match
                // https://code.google.com/p/android/issues/detail?id=37655

                var bitsperpixel = (ImageFormat.getBitsPerPixel(PreviewFormat));
                var buffersize = min.width * min.height / 8 * bitsperpixel;

                // 12
                // http://www.fourcc.org/yuv.php

                //var buffersize = 460800;

                log("" + new { min.width, min.height, bitsperpixel, buffersize });


                #region setFocusMode
                var focusModes = p.getSupportedFocusModes();
                var NextFocus = android.hardware.Camera.Parameters.FOCUS_MODE_FIXED;

                for (int i = 0; i < focusModes.size(); i++)
                {
                    var focusMode = (string)focusModes.get(i);

                    if (focusMode == android.hardware.Camera.Parameters.FOCUS_MODE_INFINITY)
                        NextFocus = android.hardware.Camera.Parameters.FOCUS_MODE_INFINITY;

                    System.Console.WriteLine(new { focusMode });
                }

                p.setFocusMode(NextFocus);
                #endregion

                camera.setParameters(p);
                #endregion


                // preview layout size: 736/1216
                buffersize = 1843200;

                // E/Camera-JNI( 3148): Manually set buffer was too small! Expected 460800 bytes, but got 307200!
                camera.addCallbackBuffer(new sbyte[buffersize]);
                camera.addCallbackBuffer(new sbyte[buffersize]);
                camera.addCallbackBuffer(new sbyte[buffersize]);
                camera.addCallbackBuffer(new sbyte[buffersize]);
                camera.addCallbackBuffer(new sbyte[buffersize]);

                var a = new EventWaitHandle(false, EventResetMode.ManualReset);

                // Task.ContinueWith
                // await

                (ScriptCoreLib.Android.ThreadLocalContextReference.CurrentContext as Activity).StartNew(
                    aa =>
                    {
                        log("at runOnUiThread...");


                        // solve issue with callback not being called: release the camera and try again. It usually works.
                        //To solve issue with rotation 0x80001005: restart app / service

                        // http://stackoverflow.com/questions/13546788/camera-takepicture-not-working-on-my-jb-gb-froyo-phones
                        aa.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);


                        #region setErrorCallback
                        camera.setErrorCallback(
                           new XErrorCallback
                           {
                               yield = (err, c) =>
                               {
                                   log("" + new { err });
                               }

                           }
                       );
                        #endregion

                        surface = new SurfaceView(ScriptCoreLib.Android.ThreadLocalContextReference.CurrentContext);

                        var holder = surface.getHolder();

                        // http://developer.android.com/reference/android/view/SurfaceHolder.html#SURFACE_TYPE_PUSH_BUFFERS
                        var SURFACE_TYPE_PUSH_BUFFERS = 0x00000003;
                        holder.setType(SURFACE_TYPE_PUSH_BUFFERS);




                        log("setPreviewCallbackWithBuffer");

                        var cc = 0;

                        //E/CameraHardwareSec(   84): int android::CameraHardwareSec::previewThread(): mSkipFrame(3) > 0
                        //E/CameraHardwareSec(   84): int android::CameraHardwareSec::previewThread(): mSkipFrame(2) > 0
                        //E/CameraHardwareSec(   84): int android::CameraHardwareSec::previewThread(): mSkipFrame(1) > 0

                        // http://stackoverflow.com/questions/16878042/camera-not-working-in-google-nexus-tablet

                        #region camera.PreviewCallbackWithBuffer
                        camera.PreviewCallbackWithBuffer(
                            (rawdata, c) =>
                            {
                                if (surface == null)
                                {
                                    // W/CameraHardwareSec(   84): virtual android::status_t android::CameraHardwareSec::cancelPicture() : not supported, just returning NO_ERROR
                                    return;
                                }

                                //                                                                   10080.0ms PreviewCallbackWithBuffer enter { cc = 58, Length = 460800 }
                                //10119.0ms PreviewCallbackWithBuffer compressToJpeg done { cc = 58, Elapsed = 39.0ms }
                                //10174.0ms PreviewCallbackWithBuffer ToBase64String done { cc = 58, Elapsed = 94.0ms }

                                var xcc = cc;

                                log("PreviewCallbackWithBuffer enter " + new { xcc, rawdata.Length });

                                // failed to flush { Length = 14619 }
                                //new Thread(
                                //    delegate()
                                {
                                    if (surface == null)
                                    {
                                        // W/CameraHardwareSec(   84): virtual android::status_t android::CameraHardwareSec::cancelPicture() : not supported, just returning NO_ERROR
                                        return;
                                    }

                                    var cst = new Stopwatch();
                                    cst.Start();

                                    // http://stackoverflow.com/questions/3426614/android-converting-from-nv21-preview-format-on-nexus-one-to-jpeg
                                    // http://developer.android.com/reference/android/graphics/YuvImage.html

                                    //Caused by: java.lang.IllegalArgumentException: only support ImageFormat.NV21 and ImageFormat.YUY2 for now
                                    //at android.graphics.YuvImage.<init>(YuvImage.java:82)


                                    // https://code.google.com/p/android/issues/detail?id=823
                                    // https://code.google.com/p/android-source-browsing/source/browse/graphics/java/android/graphics/YuvImage.java?repo=platform--frameworks--base&name=android-cts-4.1_r1
                                    var yuv = new YuvImage(
                                        rawdata,
                                        PreviewFormat,
                                        min.width,
                                        min.height,
                                        null
                                    );

                                    var m = new java.io.ByteArrayOutputStream();

                                    yuv.compressToJpeg(
                                        new Rect(0, 0, min.width, min.height),
                                        20,
                                        m);

                                    var data = (byte[])(object)m.toByteArray();

                                    log("PreviewCallbackWithBuffer compressToJpeg done " + new { xcc, cst.Elapsed });


                                    Console.WriteLine("compressToJpeg "
                                        + new { data.Length }
                                    );

                                    var src = "data:image/jpg;base64," +
                                            Convert.ToBase64String(
                                                data
                                            );

                                    log("PreviewCallbackWithBuffer ToBase64String done " + new { xcc, cst.Elapsed });

                                    y(src);


                                    //PreviewCallbackWithBuffer { cc = 0, Length = 1048576 }

                                    if (surface == null)
                                    {
                                        // W/CameraHardwareSec(   84): virtual android::status_t android::CameraHardwareSec::cancelPicture() : not supported, just returning NO_ERROR
                                        return;
                                    }

                                    //camera.addCallbackBuffer();
                                    camera.addCallbackBuffer(new sbyte[buffersize]);

                                    log("PreviewCallbackWithBuffer exit " + new { xcc, cst.Elapsed });


                                    if (xcc == frames)
                                    {

                                        //dummy.setVisibility(View.GONE);
                                        //dummy = null;

                                        // Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

                                        aa.StartNew(
                                                delegate
                                                {
                                                    if (surface != null)
                                                    {
                                                        surface.setVisibility(View.GONE);
                                                        surface = null;

                                                    }

                                                    a.Set();
                                                }
                                        );
                                    }
                                }
                                //).Start();

                                cc++;


                            }
                        );
                        #endregion


                        #region holder.surfaceChanged
                        holder.surfaceChanged(
                            delegate
                            {
                                log("surfaceChanged?");
                            }
                        );
                        #endregion

                        #region holder.surfaceCreated
                        holder.surfaceCreated(
                            delegate
                            {
                                log("surfaceCreated!");

                                // http://stackoverflow.com/questions/12098298/android-camera-app-passed-null-surface
                                // http://stackoverflow.com/questions/16945524/app-passed-null-surface-while-taking-a-picture-without-a-surfaceview

                                //  app passed NULL surface
                                log("before setPreviewDisplay, delay");

                                Thread.Sleep(400);

                                // https://code.google.com/p/zxing/source/browse/trunk/android/src/com/google/zxing/client/android/camera/CameraManager.java
                                // http://stackoverflow.com/questions/16945524/app-passed-null-surface-while-taking-a-picture-without-a-surfaceview

                                // http://stackoverflow.com/questions/4852740/surfaceview-getholder-not-returning-surfaceholder

                                log("before setPreviewDisplay " + new { holder });
                                // inside surface changed?

                                //if (surface == 0)
                                //{
                                //    LOGE("app passed NULL surface");
                                //    return NO_INIT;
                                //}

                                // https://android.googlesource.com/platform/frameworks/native/+/a6938bab1f6fa76ae98ebbe44f4e534e05fa0993/libs/ui/Camera.cpp
                                camera.setTryPreviewDisplay(holder);
                                log("after setPreviewDisplay");


                                log("startPreview, delay");
                                Thread.Sleep(200);
                                log("startPreview");

                                camera.startPreview();
                                log("after startPreview");
                            }
                        );
                        #endregion


                        #region addContentView
                        //surface.setBackgroundColor(Color.argb(0x0F, 255, 0, 0));

                        var pp = new android.widget.LinearLayout.LayoutParams(
                            android.widget.LinearLayout.LayoutParams.FILL_PARENT,
                            android.widget.LinearLayout.LayoutParams.FILL_PARENT
                            );
                        //pp.setMargins(64, 64, 64, 64);

                        aa.addContentView(surface, pp);
                        #endregion





                    }
                );

                a.WaitOne();

                log("PreviewCallbackWithBuffer done");

            }
            catch (Exception ex)
            {
                log("error: " + new { ex.Message, ex.StackTrace });

                throw new Exception("", ex);
            }
            finally
            {
                log("finally");
                // using
                if (camera != null)
                {
                    camera.stopPreview();
                    camera.release();
                    camera = null;
                }


            }
        }
        protected override void onCreate(android.os.Bundle savedInstanceState)
        {
            // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20160102/x360videos
            // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20151006
            base.onCreate(savedInstanceState);


            nativeOnCreate(this);

            //setCurrentLanguage(Locale.getDefault().getLanguage());

            // Create the SoundPool
            soundPool = new SoundPool(3 /* voices */, AudioManager.STREAM_MUSIC, 100);
            soundPoolSoundIds = new java.util.ArrayList<int>();
            soundPoolSoundNames = new java.util.ArrayList<string>();

            AudioManager audioMgr;
            audioMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
            var rate = audioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
            var size = audioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
            System.Console.WriteLine("rate = " + rate);
            System.Console.WriteLine("size = " + size);

            // Check preferences
            SharedPreferences prefs = getApplicationContext().getSharedPreferences("oculusvr", MODE_PRIVATE);
            var username = prefs.getString("username", "guest");
            System.Console.WriteLine("username = "******"action:" + intent.getAction());
            System.Console.WriteLine("type:" + intent.getType());
            System.Console.WriteLine("fromPackageName:" + fromPackageNameString);
            System.Console.WriteLine("command:" + commandString);
            System.Console.WriteLine("uri:" + uriString);

            SurfaceView sv = new SurfaceView(this);
            setContentView(sv);


            #region xCallback
            // X:\jsc.svn\examples\java\android\synergy\OVRVrCubeWorldSurfaceView\OVRVrCubeWorldSurfaceView\ApplicationActivity.cs
            var xCallback = new xCallback
            {
                onsurfaceCreated = holder =>
                {
                    if (appPtr == 0)
                        return;

                    nativeSurfaceCreated(appPtr, holder.getSurface());
                    mSurfaceHolder = holder;
                },

                onsurfaceChanged = (SurfaceHolder holder, int format, int width, int height) =>
                {
                    if (appPtr == 0)
                        return;

                    nativeSurfaceChanged(appPtr, holder.getSurface());
                    mSurfaceHolder = holder;
                },

                onsurfaceDestroyed = holder =>
                {
                    if (appPtr == 0)
                        return;

                    nativeSurfaceDestroyed(appPtr);
                    mSurfaceHolder = holder;
                }
            };
            #endregion

            sv.getHolder().addCallback(xCallback);

            // Force the screen to stay on, rather than letting it dim and shut off
            // while the user is watching a movie.
            //getWindow().addFlags( WindowManager_LayoutParams.FLAG_KEEP_SCREEN_ON );

            //// Force screen brightness to stay at maximum
            //WindowManager.LayoutParams params = getWindow().getAttributes();
            //params.screenBrightness = 1.0f;
            //getWindow().setAttributes( params );

            this.ondispatchTouchEvent += (e) =>
            {
                int action = e.getAction();
                float x = e.getRawX();
                float y = e.getRawY();
                Log.d(TAG, "onTouch dev:" + e.getDeviceId() + " act:" + action + " ind:" + e.getActionIndex() + " @ " + x + "," + y);
                nativeTouch(appPtr, action, x, y);
                return true;
            };

            this.ondispatchKeyEvent = (e) =>
            {
                bool down;
                int keyCode = e.getKeyCode();
                int deviceId = e.getDeviceId();

                if (e.getAction() == KeyEvent.ACTION_DOWN)
                {
                    down = true;
                }
                else if (e.getAction() == KeyEvent.ACTION_UP)
                {
                    down = false;
                }
                else
                {
                    Log.d(TAG,
                            "source " + e.getSource() + " action "
                                    + e.getAction() + " keyCode " + keyCode);

                    return base.dispatchKeyEvent(e);
                }

                Log.d(TAG, "source " + e.getSource() + " keyCode " + keyCode + " down " + down + " repeatCount " + e.getRepeatCount());

                if (keyCode == KeyEvent.KEYCODE_VOLUME_UP)
                {
                    if (down)
                    {
                        adjustVolume(1);
                    }
                    return true;
                }

                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
                {
                    if (down)
                    {
                        adjustVolume(-1);
                    }
                    return true;
                }


                // Joypads will register as keyboards, but keyboards won't
                // register as position classes
                // || event.getSource() != 16777232)
                // Volume buttons are source 257
                if (e.getSource() == 1281)
                {
                    // do we have one we can test with?

                    //keyCode |= JoyEvent.BUTTON_JOYPAD_FLAG;
                }
                return buttonEvent(deviceId, keyCode, down, e.getRepeatCount());
            };
        }
        protected override void onCreate(Bundle savedInstanceState)
        {
            base.onCreate(savedInstanceState);

            #region xCallback
            // X:\jsc.svn\examples\java\android\synergy\OVRVrCubeWorldSurfaceView\OVRVrCubeWorldSurfaceView\ApplicationActivity.cs
            var xCallback = new xCallback
            {
                onsurfaceCreated = holder =>
                {
                    if (mNativeHandle != 0)
                    {
                        GLES3JNILib.onSurfaceCreated(mNativeHandle, holder.getSurface());
                        mSurfaceHolder = holder;
                    }
                },

                onsurfaceChanged = (SurfaceHolder holder, int format, int width, int height) =>
                {
                    if (mNativeHandle != 0)
                    {
                        GLES3JNILib.onSurfaceChanged(mNativeHandle, holder.getSurface());
                        mSurfaceHolder = holder;
                    }
                },

                onsurfaceDestroyed = holder =>
                {
                    if (mNativeHandle != 0)
                    {
                        GLES3JNILib.onSurfaceDestroyed(mNativeHandle);
                        mSurfaceHolder = null;
                    }
                }
            };
            #endregion






            mView = new SurfaceView(this);
            this.setContentView(mView);



            //            E/AndroidRuntime(22718): Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.ViewParent android.view.View.getParent()' on a null object reference
            //E/AndroidRuntime(22718):        at android.view.ViewGroup.addViewInner(ViewGroup.java:4216)
            //E/AndroidRuntime(22718):        at android.view.ViewGroup.addView(ViewGroup.java:4070)
            //E/AndroidRuntime(22718):        at android.view.ViewGroup.addView(ViewGroup.java:4046)
            //E/AndroidRuntime(22718):        at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:478)
            //E/AndroidRuntime(22718):        at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:459)
            //E/AndroidRuntime(22718):        at android.app.Activity.setContentView(Activity.java:2298)

            var sw = Stopwatch.StartNew();

            #region mDraw
            var mDraw = new DrawOnTop(this)
            {
                // yes it appears top left.

                //text = "GearVR HUD"
                text = () => sw.ElapsedMilliseconds + "ms !"
            };

            //Task.Run(

            new Thread(
                delegate()
                {
                    // bg thread

                    while (true)
                    {
                        //Thread.Sleep(1000 / 15);
                        Thread.Sleep(1000 / 30);


                        mDraw.postInvalidate();
                    }
                }
            ).Start();
            #endregion


            this.ondispatchTouchEvent = @event =>
            {
                if (mNativeHandle == 0)
                    return;

                int action = @event.getAction();
                float x = @event.getRawX();
                float y = @event.getRawY();
                //if (action == MotionEvent.ACTION_UP)
                {
                    var halfx = 2560 / 2;
                    var halfy = 1440 / 2;

                    mDraw.x = (int)(500 + halfx - x);
                    mDraw.y = (int)(600 + y - halfy);
                    mDraw.text = () => sw.ElapsedMilliseconds + "ms \n" + new { x, y, action }.ToString();
                    //Console.WriteLine(" ::dispatchTouchEvent( " + action + ", " + x + ", " + y + " )");
                }
                GLES3JNILib.onTouchEvent(mNativeHandle, action, x, y);

                // can we move hud around and record it to gif or mp4?
            };

            this.ondispatchKeyEvent = @event =>
            {
                if (mNativeHandle == 0)
                    return false;

                int keyCode = @event.getKeyCode();
                int action = @event.getAction();
                if (action != KeyEvent.ACTION_DOWN && action != KeyEvent.ACTION_UP)
                {
                    return base.dispatchKeyEvent(@event);
                }
                if (action == KeyEvent.ACTION_UP)
                {
                    // keycode 4
                    mDraw.text = () => sw.ElapsedMilliseconds + "ms \n" + new { keyCode, action }.ToString();
                    //Log.v(TAG, "GLES3JNIActivity::dispatchKeyEvent( " + keyCode + ", " + action + " )");
                }
                GLES3JNILib.onKeyEvent(mNativeHandle, keyCode, action);

                return true;
            };

            // X:\jsc.svn\examples\java\android\synergy\OVRVrCubeWorldSurfaceView\OVRVrCubeWorldSurfaceView\ApplicationActivity.cs
            // X:\jsc.svn\examples\java\android\AndroidLacasCameraServerActivity\AndroidLacasCameraServerActivity\ApplicationActivity.cs
            addContentView(mDraw, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

            mView.getHolder().addCallback(xCallback);

            // Force the screen to stay on, rather than letting it dim and shut off
            // while the user is watching a movie.

            // does this disable the face sensor?
            getWindow().addFlags(WindowManager_LayoutParams.FLAG_KEEP_SCREEN_ON);

            // Force screen brightness to stay at maximum
            //WindowManager_LayoutParams _params = getWindow().getAttributes();
            //_params.screenBrightness = 1.0f;
            //getWindow().setAttributes(_params);

            mNativeHandle = com.oculus.gles3jni.GLES3JNILib.onCreate(this);

            // can we now overlay something on top of the surface?
        }