0

I have the following Fragment, which is called from a tab click:

public class HomeFragment : Fragment
{
    private CustomZXingFragment scanFragment;

    public override void OnStart() => base.OnStart();

    public override void OnCreate(Bundle savedInstanceState) => base.OnCreate(savedInstanceState);

    public override void OnResume()
    {
        base.OnResume();

        var needsPermissionRequest = ZXing.Net.Mobile.Android.PermissionsHandler.NeedsPermissionRequest(Model.Model.MainActivityContext);
        if (needsPermissionRequest)
            ZXing.Net.Mobile.Android.PermissionsHandler.RequestPermissionsAsync(Activity)
                .ContinueWith((b) => ShowScanFragment());
        else
            ShowScanFragment();
    }

    public override void OnViewCreated(View view, Bundle savedInstanceState) => base.OnViewCreated(view, savedInstanceState);

    private void ShowScanFragment()
    {
        MobileBarcodeScanner.Initialize(Activity.Application);

        if (scanFragment == null)
        {
            scanFragment = new CustomZXingFragment();
            FragmentManager.BeginTransaction().Replace(Resource.Id.container, scanFragment).Commit();
        }

        Scan();
    }

    private void Scan()
    {
        var opts = new MobileBarcodeScanningOptions
        {

            PossibleFormats = new List<BarcodeFormat> {
                BarcodeFormat.QR_CODE,
            },
            UseNativeScanning = true,
            TryHarder = true,
        };

        scanFragment.StartScanning(scanFragment.DoneScanning, opts);
    }

And I have the following ZXing fragment:

public class CustomZXingFragment : ZXingScannerFragment
{
    public override View OnCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle bundle)
    {
        var view = base.OnCreateView(layoutInflater, viewGroup, bundle);
        var activity = (MainActivity)Model.Model.MainActivityContext;
        return view;
    }

    public void DoneScanning(Result scannedText)
    {
        // ...
    }
}

And for some reason, only during the first time you start the app, the camera isn't starting. It shows the default red horizontal line, but the background is just completely black. If I restart the app it works just fine. I grabbed the debug output from a first launch (which shows a black background):

02-03 16:29:01.627 D/ZXing.Net.Mobile(21530): ZXingScannerFragment->OnResume exit
02-03 16:29:01.635 D/ViewRootImpl@d630583[MainActivity](21530): Relayout returned: oldFrame=[0,0][720,1280] newFrame=[0,0][720,1280] result=0x1 surface={isValid=true -885530624} surfaceGenerationChanged=false
02-03 16:29:01.652 D/SurfaceView(21530): Relayout returned: oldFrame=[0,0][0,0] newFrame=[0,48][720,1280] result=0x7 surface={Surface(name=null)/@0x8798822 isValid=true -918034432}
02-03 16:29:01.673 D/SurfaceView(21530): Relayout returned: oldFrame=[0,48][720,1280] newFrame=[0,48][720,1280] result=0x1 surface={Surface(name=null)/@0x8798822 isValid=true -925298688}

and a second start (which works fine):

02-03 16:30:38.284 D/ZXing.Net.Mobile(22786): ZXingScannerFragment->OnResume exit
02-03 16:30:38.293 D/ViewRootImpl@431432[MainActivity](22786): Relayout returned: oldFrame=[0,0][720,1280] newFrame=[0,0][720,1280] result=0x1 surface={isValid=true -878399488} surfaceGenerationChanged=false
02-03 16:30:38.316 D/SurfaceView(22786): Relayout returned: oldFrame=[0,0][0,0] newFrame=[0,48][720,1280] result=0x7 surface={Surface(name=null)/@0x393638a isValid=true -914219008}
02-03 16:30:38.316 D/ZXing.Net.Mobile(22786): Checking android.permission.CAMERA...
02-03 16:30:38.319 D/ZXing.Net.Mobile(22786): Checking Number of cameras...
02-03 16:30:38.320 I/ServiceManager(22786): Waiting for service media.camera...
02-03 16:30:38.324 I/art     (22786): Do partial code cache collection, code=23KB, data=28KB
02-03 16:30:38.324 I/art     (22786): After code cache collection, code=19KB, data=24KB
02-03 16:30:38.324 I/art     (22786): Increasing code cache capacity to 128KB
02-03 16:30:39.322 D/ZXing.Net.Mobile(22786): Found 2 cameras...
02-03 16:30:39.325 D/ZXing.Net.Mobile(22786): Found Back Camera, opening...
02-03 16:30:39.404 D/ZXing.Net.Mobile(22786): Selected Resolution: 960x720
02-03 16:30:39.412 D/ZXing.Net.Mobile(22786): Changing Camera Orientation to: 90
02-03 16:30:39.822 D/ZXing.Net.Mobile(22786): Selected Resolution: 960x720
02-03 16:30:39.831 D/ZXing.Net.Mobile(22786): Changing Camera Orientation to: 90
02-03 16:30:39.846 I/Choreographer(22786): Skipped 92 frames!  The application may be doing too much work on its main thread.
02-03 16:30:39.856 D/SurfaceView(22786): Relayout returned: oldFrame=[0,48][720,1280] newFrame=[0,48][720,1280] result=0x1 surface={Surface(name=null)/@0x393638a isValid=true -890101760}
02-03 16:30:42.144 I/art     (22786): Starting a blocking GC Explicit
02-03 16:30:42.150 D/Mono    (22786): GC_BRIDGE waiting for bridge processing to finish
02-03 16:30:42.198 I/art     (22786): Explicit concurrent mark sweep GC freed 4669(256KB) AllocSpace objects, 5(4MB) LOS objects, 39% free, 8MB/14MB, paused 532us total 54.160ms
02-03 16:30:42.201 D/Mono    (22786): GC_TAR_BRIDGE bridges 319 objects 388 opaque 66 colors 322 colors-bridged 319 colors-visible 319 xref 9 cache-hit 0 cache-semihit 0 cache-miss 3 setup 0.13ms tarjan 0.34ms scc-setup 0.23ms gather-xref 0.02ms xref-setup 0.02ms cleanup 0.12ms
02-03 16:30:42.201 D/Mono    (22786): GC_BRIDGE: Complete, was running for 59.51ms
02-03 16:30:42.201 D/Mono    (22786): GC_MAJOR: (LOS overflow) time 11.64ms, stw 12.98ms los size: 3072K in use: 765K
02-03 16:30:42.201 D/Mono    (22786): GC_MAJOR_SWEEP: major size: 1312K in use: 485K

As you can see, there's a discrepancy, but I don't know why ZXing isn't starting the camera in the first one. Permissions are fine, I've already checked that.

Can anyone help me out?

Viggo Lundén
  • 748
  • 1
  • 6
  • 31

2 Answers2

1

I've had the same issue when ZXingScannerFragment shows black screen at the very first app launch. After an investigation I found a remedy. I've just replace .Commit(); with .CommitAllowingStateLoss(); in method which creates ZxingFragment.

SupportFragmentManager.BeginTransaction()
    .Replace(Resource.Id.fragment_container, _zXingScannerFragment)
    .CommitAllowingStateLoss(); 

I found out that @pnavk assumption about camera granting permission was right. Even after the using the await PermissionsHandler.RequestPermissionsAsync(this); the PermissionsHandler.PermissionRequestTask was not completed successfully.

The complete solution of the issue would be the using third-party code to maintain the process of granting permissions instead of ZXing built-in. I checked PermissionsPlugin by James Montemagno and it works perfectly. But it requires to make too more code changes and add additional plugin, so I decided to use simple solution. Maybe it helps in your case.

SergICE
  • 181
  • 1
  • 9
0

Because this only happens the first time around, It sounds like it's caused by the call to RequestPermissionsAsync. I am assuming you accept the permission the first time and so the second time around the needsPermissionRequest condition resolves to false and RequestPermissionsAsync is not invoked and so the code works.

There are a few modifications you should make to the code:

ZXing.Net.Mobile.Android.PermissionsHandler.RequestPermissionsAsync(Activity)
                .ContinueWith((b) => ShowScanFragment());

You should await this async method call instead of using ContinueWith. ShowScanFragment is being invoked in a background thread instead of the UI thread which appears to be causing your issue.

You can also remove the following line:

var needsPermissionRequest = ZXing.Net.Mobile.Android.PermissionsHandler.NeedsPermissionRequest(Model.Model.MainActivityContext);

If you check the ZXing source code this check is already done for you when you call RequestPermissionsAsync so you don't need to do it yourself.

Try modifying your OnResume method as such:

public override async void OnResume()
{
    base.OnResume();

    var permissionGranted = await ZXing.Net.Mobile.Android.PermissionsHandler.RequestPermissionsAsync(Activity);
    if(permissionGranted) {
        ShowScanFragment();
    }else{
        //handle permission denied
    }
}
pnavk
  • 4,552
  • 2
  • 19
  • 43
  • Thanks for the help. However it's not working. Looks like you're correct regarding the permission checks, it won't prompt unless it have to. But apparently, it changes nothing. ShowScanFragment() is still called like normal, but without the camera. Restarting the app works around the problem. – Viggo Lundén Feb 03 '19 at 22:43
  • @ViggoLundén try setting a breakpoint on ShowScanFragment to make sure it’s not being called twice. I know OnResume on Android gets called again after a permission prompt is dismissed. That might be what’s causing the problem – pnavk Feb 03 '19 at 22:47
  • I experimented a little bit the permissions, so I moved the permission request to another fragment, and moved `ShowScanFragment()` to `OnViewCreated()` instead. The behavior remains the same. I can't help but feel like this isn't related to permissions. – Viggo Lundén Feb 03 '19 at 23:12