nstudio / nativescript-camera-plus

MIT License
79 stars 50 forks source link

Preview rotated when app isn't in natural orientation - Android API 21+ / CameraX #144

Open Murilo-Perrone opened 3 years ago

Murilo-Perrone commented 3 years ago

Whenever the view is rotated, preview image must rotate accordingly. Since that's not happening, we get a rotated preview window: pic2

Experments in Fancycamera component with preview.setRotation(rotation: Int) were noneffective. This is actually a naive code problem, as mentioned in this stackoverflow topic. As explained in Husayn Hakeem's blog post, the recommended solution is to use PreviewView class from CameraX (see the implementation). Question is how to do that in a nativescript plugin?

NativeScript does not have a surface view, so the solution may to through programmatic binding of a child view. Possibly through NativeScript's PlaceHolder element, which allows the adding any native widget to the application.

triniwiz commented 3 years ago

We can use the preview view by extending that instead of the textureview but they didn’t have a way to interact with the preview view using OpenGL which I would like to do soon to add support in the canvas plugin

Murilo-Perrone commented 3 years ago

Hmm now I see. Unfortunately PreviewView is final, so it can't be extended. And this guide indicates we can't mix our own surface or TextureView with a PreviewView.

So this would be a huge change:

The PreviewView class applies a transformation matrix to the surface, and we can't copy that code because the methods are all internal and the code is too complex anyway. It would be easier if google made Preview.Builder.setTargetRotation or Preview.setTargetRotation to work readily with our TextureView. But that doesn't seem to be on their plans. So there is no middle ground.

For immediate results, I tried handling it all within camera-plus, based on View.setRotation. That seems doable, although it is a cheap workaround that would require a few adjustments to show up right. In the image below, I rotated FancyCamera and the buttons, but would still need to recalculate their locations (note they are in wrong position):

TestCam

Thoughts about this approach? My wonder is code maintenance.

sublime392 commented 3 years ago

Update: nvm, rotation seems to be working with Fancy Camera 3.0.0-alpha15.

@Murilo-Perrone can you give me a few more details about how you did your rotation? I need something that I can do right now, and what you have seems good enough. I don't need any details related to the buttons as I am already using my own. I just need to know how/where you set the rotation in camera-plus. Thanks!

Murilo-Perrone commented 3 years ago

@sublime392 hi sorry for the delay. That tentative is based on rough view rotation, such as the CSS 'rotate' property:

public camLoaded(e: any): void {
  this.cam = e.object as CameraPlus;
  if (isAndroid) {
    this.cam.rotate = 90;

You can get same result through this.cam.nativeView.setRotation(90). This can respond to orientation changes detected by nativescript component:

constructor(private zone: NgZone) {
    on("orientationChanged",  (evt) => {
      setTimeout(() => this.onOrientationChanged(evt.eventName, evt.newValue), 1000)
    });
}
onOrientationChanged(eventName: string, newValue: string) {
    if (newValue == "landscape") {
        ...
    }
    if (newValue == "portrait") {
        ...
    }
}

In case you want to seek for a workaround within the plugin code, the above detection may be done with native code, as described in issue 143 . Then other adjustments can be tried in camera-plus.android.ts. Here is an example:

private _onLayoutChangeFn(...args: any[]) {
  console.log("_onLayoutChangeFn: ", args);
  const size = this.getActualSize();
  CLog('xml width/height:', size.width + 'x' + size.height);
  this.adjustRotation(); // <== NEW CODE
  this._initDefaultButtons();
}

private adjustRotation() {
  const viewAngle = this.nativeView.getDisplay().getRotation() * 90;
  CLog("viewAngle: " + viewAngle);
  // this.rotate = 360 - viewAngle;
  this._nativeView.setRotation(360 - viewAngle);
  const oldHeight = this._nativeView.height;
  this._nativeView.height = this._nativeView.width;
  this._nativeView.width = oldHeight;

  if (this._flashBtn) this._flashBtn.setRotation(viewAngle);
  if (this._takePicBtn) this._takePicBtn.setRotation(viewAngle);
  if (this._toggleCamBtn) this._toggleCamBtn.setRotation(viewAngle);
  if (this._galleryBtn) this._galleryBtn.setRotation(viewAngle);
  this._nativeView.requestLayout();
}