title: CameraX change from alpha-04 to beta-01 broke the code
body: <p>I have added CameraX to my ongoing development app a while ago. I know it was in alpha but I was ready to make the change when beta or final release will be available.</p>
  
  <p>So I started working on it today. I have updated from</p>
  
  <pre><code>implementation 'androidx.camera:camera-core:1.0.0-alpha04'
  implementation 'androidx.camera:camera-camera2:1.0.0-alpha04'
  </code></pre>
  
  <p>to this:</p>
  
  <pre><code>implementation 'androidx.camera:camera-core:1.0.0-beta01'
  implementation 'androidx.camera:camera-camera2:1.0.0-beta01'
  implementation 'androidx.camera:camera-lifecycle:1.0.0-beta01'
  </code></pre>
  
  <p>My Previous Working Code (alpha-04):</p>
  
  <pre><code>class ScannerX : AppCompatActivity() {
  private lateinit var context: Context
  
  var isOtpAuthCode = true
  
  private val immersiveFlagTimeout = 500L
  private val flagsFullscreen = View.SYSTEM_UI_FLAG_LOW_PROFILE or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
          View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
  
  private var preview: Preview? = null
  private var lensFacing = CameraX.LensFacing.BACK
  private var imageAnalyzer: ImageAnalysis? = null
  
  private lateinit var analyzerThread: HandlerThread
  
  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_scanner_x)
  
      context = this
  
      btnCancel.setOnClickListener {
          finish()
      }
  
      analyzerThread = if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) {
          HandlerThread("BarcodeFirebaseAnalyzer").apply { start() }
      } else {
          HandlerThread("BarcodeZxingAnalyzer").apply { start() }
      }
  
      Dexter.withActivity(this)
          .withPermissions(Manifest.permission.CAMERA)
          .withListener(object : MultiplePermissionsListener {
              override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
                  textureView.post {
                      val metrics = DisplayMetrics().also { textureView.display.getRealMetrics(it) }
                      val screenAspectRatio = Rational(metrics.widthPixels, metrics.heightPixels)
  
                      val previewConfig = PreviewConfig.Builder().apply {
                          setLensFacing(lensFacing)
                          // We request aspect ratio but no resolution to let CameraX optimize our use cases
                          setTargetAspectRatio(screenAspectRatio)
                          // Set initial target rotation, we will have to call this again if rotation changes
                          // during the lifecycle of this use case
                          setTargetRotation(textureView.display.rotation)
                      }.build()
  
                      val analyzerConfig = ImageAnalysisConfig.Builder().apply {
                          setLensFacing(lensFacing)
                          // Use a worker thread for image analysis to prevent preview glitches
                          setCallbackHandler(Handler(analyzerThread.looper))
                          // In our analysis, we care more about the latest image than analyzing *every* image
                          setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
                          // Set initial target rotation, we will have to call this again if rotation changes
                          // during the lifecycle of this use case
                          setTargetRotation(textureView.display.rotation)
                      }.build()
  
                      preview = AutoFitPreviewBuilder.build(previewConfig, textureView)
  
                      imageAnalyzer = ImageAnalysis(analyzerConfig).apply {
                          analyzer = if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) {
                              BarcodeFirebaseAnalyzer { qrCode -&gt;
                                  if (isOtpAuthCode) {
                                      if (qrCode.startsWith("otpauth")) {
                                          toAddAuth(qrCode)
                                      }
                                  } else {
                                      toAddAuth(qrCode)
                                  }
                              }
                          } else {
                              BarcodeZxingAnalyzer { qrCode -&gt;
                                  if (isOtpAuthCode) {
                                      if (qrCode.startsWith("otpauth")) {
                                          toAddAuth(qrCode)
                                      }
                                  } else {
                                      toAddAuth(qrCode)
                                  }
                              }
                          }
                      }
  
                      // Apply declared configs to CameraX using the same lifecycle owner
                      CameraX.bindToLifecycle(this@ScannerX, preview, imageAnalyzer)
                  }
              }
  
              override fun onPermissionRationaleShouldBeShown(permissions: MutableList&lt;PermissionRequest&gt;?, token: PermissionToken?) {
                  //
              }
          }).check()
  }
  
  override fun onStart() {
      super.onStart()
      // Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may
      // be trying to set app to immersive mode before it's ready and the flags do not stick
      textureView.postDelayed({
          textureView.systemUiVisibility = flagsFullscreen
      }, immersiveFlagTimeout)
  }
  
  override fun onDestroy() {
      analyzerThread.quit()
      super.onDestroy()
  }
  
  private fun toAddAuth(scannedCode: String) {
      if (CameraX.isBound(imageAnalyzer)) {
          CameraX.unbind(imageAnalyzer)
      }
  
      val intent = Intent()
      intent.putExtra("scanResult", scannedCode)
      setResult(RESULT_OK, intent)
      finish()
  }
  
  companion object {
      private const val RESULT_OK = 666
  }
  }
  </code></pre>
  
  <p>And the code I have changed is as follows (beta-01):</p>
  
  <pre><code>class ScannerX : AppCompatActivity() {
  private lateinit var context: Context
  
  var isOtpAuthCode = true
  
  private val immersiveFlagTimeout = 500L
  private val flagsFullscreen = View.SYSTEM_UI_FLAG_LOW_PROFILE or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
          View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
  
  private var preview: Preview? = null
  private var lensFacing = CameraSelector.DEFAULT_BACK_CAMERA
  private var imageAnalyzer: ImageAnalysis? = null
  
  private lateinit var analysisExecutor: ExecutorService
  private lateinit var processCameraProvider: ListenableFuture&lt;ProcessCameraProvider&gt;
  
  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_scanner_x)
  
      context = this
  
      btnCancel.setOnClickListener {
          finish()
      }
  
      Dexter.withActivity(this)
          .withPermissions(Manifest.permission.CAMERA)
          .withListener(object : MultiplePermissionsListener {
              override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
                  textureView.post {
                      analysisExecutor = Executors.newSingleThreadExecutor()
                      processCameraProvider = ProcessCameraProvider.getInstance(context)
  
                      preview = Preview.Builder()
                          .setTargetAspectRatio(AspectRatio.RATIO_16_9)
                          .setTargetRotation(textureView.display.rotation)
                          .build()
  
                      imageAnalyzer = ImageAnalysis.Builder()
                          .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                          .setTargetRotation(textureView.display.rotation)
                          .build()
  
                      if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) {
                          imageAnalyzer?.apply {
                              setAnalyzer(analysisExecutor, BarcodeFirebaseAnalyzer { qrCode -&gt;
                                  if (isOtpAuthCode) {
                                      if (qrCode.startsWith("otpauth")) {
                                          toAddAuth(qrCode)
                                      }
                                  } else {
                                      toAddAuth(qrCode)
                                  }
                              })
                          }
                      } else {
                          imageAnalyzer?.apply {
                              setAnalyzer(analysisExecutor, BarcodeZxingAnalyzer { qrCode -&gt;
                                  if (isOtpAuthCode) {
                                      if (qrCode.startsWith("otpauth")) {
                                          toAddAuth(qrCode)
                                      }
                                  } else {
                                      toAddAuth(qrCode)
                                  }
                              })
                          }
                      }
  
                      processCameraProvider.get().bindToLifecycle(this@ScannerX, lensFacing, imageAnalyzer)
                  }
              }
  
              override fun onPermissionRationaleShouldBeShown(permissions: MutableList&lt;PermissionRequest&gt;?, token: PermissionToken?) {
                  //
              }
          }).check()
  }
  
  override fun onStart() {
      super.onStart()
      // Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may
      // be trying to set app to immersive mode before it's ready and the flags do not stick
      textureView.postDelayed({
          textureView.systemUiVisibility = flagsFullscreen
      }, immersiveFlagTimeout)
  }
  
  override fun onDestroy() {
      if (!analysisExecutor.isShutdown) {
          analysisExecutor.shutdown()
      }
  
      super.onDestroy()
  }
  
  private fun toAddAuth(scannedCode: String) {
      /*if (CameraX.isBound(imageAnalyzer)) {
          CameraX.unbind(imageAnalyzer)
      }*/
  
      val intent = Intent()
      intent.putExtra("scanResult", scannedCode)
      setResult(RESULT_OK, intent)
      finish()
  }
  
  companion object {
      private const val RESULT_OK = 666
  }
  }
  </code></pre>
  
  <p>After I upgraded there were so many changes in library and now I cant make it work.</p>
  
  <p>I also cant use Google Provided AutoFitPreview Class along with initial alpha release of this library. This was not necessary even with alpha04 since the only problem without this class was camera view little bit stretched out but scanning and analyzing worked properly.</p>
  
  <pre><code>/**
  * Builder for [Preview] that takes in a [WeakReference] of the view finder and [PreviewConfig],
  * then instantiates a [Preview] which automatically resizes and rotates reacting to config changes.
  */
  class AutoFitPreviewBuilder private constructor(config: PreviewConfig, viewFinderRef: WeakReference&lt;TextureView&gt;) {
  
  /** Public instance of preview use-case which can be used by consumers of this adapter */
  val useCase: Preview
  
  /** Internal variable used to keep track of the use case's output rotation */
  private var bufferRotation: Int = 0
  
  /** Internal variable used to keep track of the view's rotation */
  private var viewFinderRotation: Int? = null
  
  /** Internal variable used to keep track of the use-case's output dimension */
  private var bufferDimens: Size = Size(0, 0)
  
  /** Internal variable used to keep track of the view's dimension */
  private var viewFinderDimens: Size = Size(0, 0)
  
  /** Internal variable used to keep track of the view's display */
  private var viewFinderDisplay: Int = -1
  
  /** Internal reference of the [DisplayManager] */
  private lateinit var displayManager: DisplayManager
  
  /**
   * We need a display listener for orientation changes that do not trigger a configuration
   * change, for example if we choose to override config change in manifest or for 180-degree
   * orientation changes.
   */
  private val displayListener = object : DisplayManager.DisplayListener {
      override fun onDisplayAdded(displayId: Int) = Unit
      override fun onDisplayRemoved(displayId: Int) = Unit
      override fun onDisplayChanged(displayId: Int) {
          val viewFinder = viewFinderRef.get() ?: return
          if (displayId == viewFinderDisplay) {
              val display = displayManager.getDisplay(displayId)
              val rotation = getDisplaySurfaceRotation(display)
              updateTransform(viewFinder, rotation, bufferDimens, viewFinderDimens)
          }
      }
  }
  
  init {
      // Make sure that the view finder reference is valid
      val viewFinder = viewFinderRef.get() ?:
      throw IllegalArgumentException("Invalid reference to view finder used")
  
      // Initialize the display and rotation from texture view information
      viewFinderDisplay = viewFinder.display.displayId
      viewFinderRotation = getDisplaySurfaceRotation(viewFinder.display) ?: 0
  
      // Initialize public use-case with the given config
      useCase = Preview(config)
  
      // Every time the view finder is updated, recompute layout
      useCase.onPreviewOutputUpdateListener = Preview.OnPreviewOutputUpdateListener {
          val viewFinderI = viewFinderRef.get() ?: return@OnPreviewOutputUpdateListener
          Log.d(TAG, "Preview output changed. " +
                  "Size: ${it.textureSize}. Rotation: ${it.rotationDegrees}")
  
          // To update the SurfaceTexture, we have to remove it and re-add it
          val parent = viewFinderI.parent as ViewGroup
          parent.removeView(viewFinderI)
          parent.addView(viewFinderI, 0)
  
          // Update internal texture
          viewFinderI.surfaceTexture = it.surfaceTexture
  
          // Apply relevant transformations
          bufferRotation = it.rotationDegrees
          val rotation = getDisplaySurfaceRotation(viewFinderI.display)
          updateTransform(viewFinderI, rotation, it.textureSize, viewFinderDimens)
      }
  
      // Every time the provided texture view changes, recompute layout
      viewFinder.addOnLayoutChangeListener { view, left, top, right, bottom, _, _, _, _ -&gt;
          val viewFinderII = view as TextureView
          val newViewFinderDimens = Size(right - left, bottom - top)
          Log.d(TAG, "View finder layout changed. Size: $newViewFinderDimens")
          val rotation = getDisplaySurfaceRotation(viewFinderII.display)
          updateTransform(viewFinderII, rotation, bufferDimens, newViewFinderDimens)
      }
  
      // Every time the orientation of device changes, recompute layout
      // NOTE: This is unnecessary if we listen to display orientation changes in the camera
      //  fragment and call [Preview.setTargetRotation()] (like we do in this sample), which will
      //  trigger [Preview.OnPreviewOutputUpdateListener] with a new
      //  [PreviewOutput.rotationDegrees]. CameraX Preview use case will not rotate the frames for
      //  us, it will just tell us about the buffer rotation with respect to sensor orientation.
      //  In this sample, we ignore the buffer rotation and instead look at the view finder's
      //  rotation every time [updateTransform] is called, which gets triggered by
      //  [CameraFragment] display listener -- but the approach taken in this sample is not the
      //  only valid one.
      displayManager = viewFinder.context
          .getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
      displayManager.registerDisplayListener(displayListener, null)
  
      // Remove the display listeners when the view is detached to avoid holding a reference to
      //  it outside of the Fragment that owns the view.
      // NOTE: Even though using a weak reference should take care of this, we still try to avoid
      //  unnecessary calls to the listener this way.
      viewFinder.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
          override fun onViewAttachedToWindow(view: View?) =
              displayManager.registerDisplayListener(displayListener, null)
          override fun onViewDetachedFromWindow(view: View?) =
              displayManager.unregisterDisplayListener(displayListener)
      })
  }
  
  /** Helper function that fits a camera preview into the given [TextureView] */
  private fun updateTransform(textureView: TextureView?, rotation: Int?, newBufferDimens: Size, newViewFinderDimens: Size) {
      // This should not happen anyway, but now the linter knows
      val textureViewI = textureView ?: return
  
      if (rotation == viewFinderRotation &amp;&amp;
          Objects.equals(newBufferDimens, bufferDimens) &amp;&amp;
          Objects.equals(newViewFinderDimens, viewFinderDimens)) {
          // Nothing has changed, no need to transform output again
          return
      }
  
      if (rotation == null) {
          // Invalid rotation - wait for valid inputs before setting matrix
          return
      } else {
          // Update internal field with new inputs
          viewFinderRotation = rotation
      }
  
      if (newBufferDimens.width == 0 || newBufferDimens.height == 0) {
          // Invalid buffer dimens - wait for valid inputs before setting matrix
          return
      } else {
          // Update internal field with new inputs
          bufferDimens = newBufferDimens
      }
  
      if (newViewFinderDimens.width == 0 || newViewFinderDimens.height == 0) {
          // Invalid view finder dimens - wait for valid inputs before setting matrix
          return
      } else {
          // Update internal field with new inputs
          viewFinderDimens = newViewFinderDimens
      }
  
      val matrix = Matrix()
      Log.d(TAG, "Applying output transformation.\n" +
              "View finder size: $viewFinderDimens.\n" +
              "Preview output size: $bufferDimens\n" +
              "View finder rotation: $viewFinderRotation\n" +
              "Preview output rotation: $bufferRotation")
  
      // Compute the center of the view finder
      val centerX = viewFinderDimens.width / 2f
      val centerY = viewFinderDimens.height / 2f
  
      // Correct preview output to account for display rotation
      matrix.postRotate(-viewFinderRotation!!.toFloat(), centerX, centerY)
  
      // Buffers are rotated relative to the device's 'natural' orientation: swap width and height
      val bufferRatio = bufferDimens.height / bufferDimens.width.toFloat()
  
      val scaledWidth: Int
      val scaledHeight: Int
      // Match longest sides together -- i.e. apply center-crop transformation
      if (viewFinderDimens.width &gt; viewFinderDimens.height) {
          scaledHeight = viewFinderDimens.width
          scaledWidth = (viewFinderDimens.width * bufferRatio).roundToInt()
      } else {
          scaledHeight = viewFinderDimens.height
          scaledWidth = (viewFinderDimens.height * bufferRatio).roundToInt()
      }
  
      // Compute the relative scale value
      val xScale = scaledWidth / viewFinderDimens.width.toFloat()
      val yScale = scaledHeight / viewFinderDimens.height.toFloat()
  
      // Scale input buffers to fill the view finder
      matrix.preScale(xScale, yScale, centerX, centerY)
  
      // Finally, apply transformations to our TextureView
      textureViewI.setTransform(matrix)
  }
  
  companion object {
      private val TAG = AutoFitPreviewBuilder::class.java.simpleName
  
      /** Helper function that gets the rotation of a [Display] in degrees */
      fun getDisplaySurfaceRotation(display: Display?) = when(display?.rotation) {
          Surface.ROTATION_0 -&gt; 0
          Surface.ROTATION_90 -&gt; 90
          Surface.ROTATION_180 -&gt; 180
          Surface.ROTATION_270 -&gt; 270
          else -&gt; null
      }
  
      /**
       * Main entry point for users of this class: instantiates the adapter and returns an instance
       * of [Preview] which automatically adjusts in size and rotation to compensate for
       * config changes.
       */
      fun build(config: PreviewConfig, viewFinder: TextureView) =
          AutoFitPreviewBuilder(config, WeakReference(viewFinder)).useCase
  }
  }
  </code></pre>
  
  <p>Please Help</p>