Need a MirrorFragment in NestedPresentationFragment

I have two fragments on main display and by using nestedPresentationFragment, I can insert two fragments on presentation display. However, I realized that my drawing fragment is so complicated that it is difficult to maintain synchronization between two instances of drawing fragment. So I looked into Mirror.

While one fragment in nestedPresentationFragment stays the same, I would like to have a MirrorFragment in nestedPresentationFragment that mirrors the drawing fragment on main display.

The current MirrorPresentationFragment doesn’t seem to work inside of nestedPresentationFragment. I have tried to implement my own MirrorFragment. The errors include strange null object reference and the presentation mode doesn’t exit cleanly and the presentation mode can only be shown the first time after reboot and will be black screen after the second run.

Any suggestions on MirrorFragment in nestedPresentationFragment? Thanks!

Not really. You have gone far beyond what I have ever tried with that code.

Off the cuff, though, I would not use a MirrorFragment, as it would appear to add no value to you in this situation, compared to just using a Mirror:

  • Have the drawing fragment’s UI that needs to be mirrored be wrapped in a MirrorFrameLayout
  • Have the Presentation show a Mirror (perhaps among other things)
  • Connect the Mirror to the MirrorFrameLayout

Hi Murphy,

Thank you for suggestion. I have tried both ways, Mirror and MirrorFragment, both in nestedPresentationFragment. In either case, the mirror is created in Fragment.onCreateView() too late after source.setMirror(m) is called. I managed to move this setMirror() to a runnable in OnCreateView(), then I got low-level error from MirroringFrameLayout. The bitmap is null in the source. Haven’t figured out what went wrong…

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.eraseColor(int)' on a null object reference
    at com.commonsware.cwac.layouts.MirroringFrameLayout.draw(MirroringFrameLayout.java:102)

I don’t really have a way of helping you debug this sort of thing, as I have no access to your code.

If the Mirror is in a fragment, and that Mirror is created as part of onCreateView() processing (e.g., inflating a layout), you should be be able to call source.setMirror() in onViewCreated() of that fragment. Or, more likely, given your setup, you would call some method on your activity, passing it your Mirror, and the activity would pass that Mirror to the fragment with the MirroringFrameLayout. Regardless, wait until onViewCreated() of the fragment with the Mirror to start trying to use the Mirror.

If there are minor tweaks that I can make to the library that will help you in your scenario, describe them, and I will see what i can do.

Hi Mark,

I figured out the solution. OnViewCreated() doesn’t work because it is called right after onCreateView() and the Mirror inside is still not inflated. I have to wait for longer time to check if the Mirror is ready.

The second “null object reference error” can be fixed by additional line in MirroringFragment. It seems to me that the bitmap inside of MirroringFrameLayout is sometimes not valid. Additional initialization can fix it.

public void setMirror(Mirror mirror) {
source.setMirror(mirror);
aspectLock.setAspectRatioSource((View)mirror);
source.onScrollChanged();
}

That is some issue specific to your layouts, apparently. For example, if the Mirror is in a nested fragment, configuration of that Mirror should be handled by the nested fragment, not the outer fragment.

IMHO, apps and libraries should not call onScrollChanged() — that is a method for the framework to call.

Hi Mark,

Thank you very much for quick reply. I am getting close to the solution.

For the timing of the mirror, I implemented a callback in onSizeChanged() on the mirror so that I can setSource at the right time. It works for the demo app now.

For the second issue (null object reference), you are right. The root cause is draw() in MirroringFrameLayout. “bmp” is used without checking if it is initialized. Somehow, the demo app works but my own app doesn’t. bmp is only initialized in onSizeChanged(). I observed that it is not called before draw() is called. Will update once I got more progress.

@Override
public void draw(Canvas canvas) {
if (mirror != null) {
bmp.eraseColor(0);

  super.draw(bmpBackedCanvas);
  getDrawingRect(rect);
  canvas.drawBitmap(bmp, null, rect, null);
  mirror.update(bmp);
}
else {
  super.draw(canvas);
}

}

That is absolutely a bug. I am surprised it has not been a problem before now. I will work on fixing it shortly. Thanks for pointing this out!

In initBitmap(), you check if the mirror is valid before initializing bitmap. So if the mirror is not ready before source is calling onSizeChanged(), the bitmap will not be initialized. After the mirror is ready and set, draw() will trigger the crash on this line.

My suggestion is to initBitmap() in SetMirror().

I do not necessarily have the size at that point. I settled for just adding a null check for the Bitmap before trying to use it. I pushed out v0.4.5 with that fix.

Let me know if you encounter further problems!

Hi Mark,

The patch you added fixed the crash. But will not display mirrored content, because bmp stays uninitialized if the source view is ready before the mirror view is ready. In addition, I added the code as following:

public void setMirror(MirrorSink mirror) {
this.mirror=mirror;

**if(bmp == null) {**

** int w=getWidth();**
** int h=getHeight();**
** if(w>0 && h>0) **
** initBitmap(w, h);**
}
if (mirror != null) {
setAspectRatioSource(mirror);
}
}

I am working on the latency issue right now. I noticed it is 200-300ms additional delay after everything is working, which is not good for my project. Will update later.

Hi Mark,

I figured out the latency issue. It is due to Mirror. Will explain in your layout project.