Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App foundation: introducing CameraX #23

Merged
merged 16 commits into from
Aug 14, 2019
Merged

Conversation

mzorz
Copy link
Contributor

@mzorz mzorz commented Aug 9, 2019

NOTE: please after reviewing, review and merge #25 first before merging this one 👍

This PR introduces Android Jetpack's Camera X. It's important to note the VideoCapture use case is still under the RestrictedApi annotation, so using it here carefully hoping they'll make it public soon.

This PR also introduces some simplification of the abstraction of a VideoRecorderFragment, which has the capabilities implemented by the interface VideoRecorderHandler:

  • startRecordingVideo()
  • stopRecordingVideo()

and interface SurfaceFragmentHandler:

  • activate()
  • deactivate()

This way, we kept both the Camera2 API implementation in Camera2BasicHandling class, and encapsulated the CameraX API implementation within the CameraXBasicHandling class, both of which are nothing else but instances of the aforementioned VideoRecorderFragment.
This also led to a simplification in the BackgroundSurfaceManager code, which is now easier to read and should pretty much take care of all the basic background surfaces handling such as turning the camera on/off, start/stop recording, and switch background surfaces (static, video player, camera preview).

Also this PR introduces a BuildConfig.USE_CAMERAX flag in gradle.properties, which is used in order to use one API or the other depending on this setting.

This way, should we need to fall back to the stable Camera2 API we can do so, by just turning the flag.

This PR introduces only the following two use cases, to just be able to match the current functionality in the app:

  • preview, and
  • VideoCapture (not documented as of yet)

In a follow up PR we'll implement the still image capture use case as well (it's coded in our Camera2 fragment handler implementation as well, but needs to be wired up).
Note there of course may be differences in handling to our own implementation, but we'll be able to choose among each of the alternatives.

To Test

  1. start the app
  2. go to the Composer activity (press on the floating action button)
  3. in the overflow menu, tap on Camera preview. Check you see the camera view.
  4. tap on the Camera preview option in the overflow menu again to START recording (note no indication that recording started is shown so, you'll have to believe)
  5. tap on the Camera preview option in the overflow menu a third time to STOP recording. This will silently save the original recording file.
  6. draw some emoji or text
  7. tap on Save option (last option in the menu)
  8. new video should have been composed.

Copy link
Collaborator

@aforcier aforcier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall and works well 👍 Just a few comments, some are just Kotlin FYIs as we discussed 😀

}

fun getCurrentVideoFile(): File? {
return camera2BasicHandler.currentFile
return cameraBasicHandler.currentFile
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some weird indentation showed up here.

Copy link
Contributor Author

@mzorz mzorz Aug 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 addressed in: bf3b76a

// the photoEditorView layout has been recreated so, re-assign its TextureView
cameraBasicHandler.textureView = photoEditorView.textureView
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably missing something - could you explain why in the CAMERAX case we don't need to add a texture listener here as in the other cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent question! It's all taken care of by the lifecycle binding CameraX uses so, we only need to tell the CameraX use case what we want from it, for example start recording video (or stopping an in-progress recording), or starting feeding the surface with the images being buffered from the camera preview, etc.

Copying from the referenced documentation in the link above for easy reading:

Instead of an application placing specific start and stop method calls in onResume() and onPause(), the application specifies a lifecycle to associate the camera with, using CameraX.bindToLifecycle(). The startup, shutdown, and production of data are governed by the specified Android Architecture Lifecycle. That lifecycle then informs CameraX when to configure the camera capture session and ensures camera state changes appropriately to match lifecycle transitions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I follow - I don't think it clicked for me that the texture listener hook was for lifecycle management. Thanks for the details, super helpful 👍

@@ -23,11 +29,16 @@ class BackgroundSurfaceManager(
private val savedInstanceState: Bundle?,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The activity val here in the constructor seems to be unused.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, I also spotted and addressed it in this other PR #26 in commits 258557f and 3c49776

@@ -42,3 +42,40 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

android.buildTypes.all { buildType ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this looks good, though I think we should take care of adding gradle.properties to .gitignore sooner than later and add a gradle.properties-example file. I'll open an issue to track that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened: #27.

def inputFile = checkGradlePropertiesFile()
def properties = loadPropertiesFromFile(inputFile)
properties.any { property ->
if (property.key.toLowerCase().startsWith("portkey.use.")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting - in WPAndroid we don't have this handling and instead just use BuildConfig.VALUE.toBoolean() when accessing it. This is probably better 😀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH that was a TIL for me as well when I introduced / borrowed these scripts :D

// setOnVideoSizeChangedListener(this)
setAudioStreamType(AudioManager.STREAM_MUSIC)
start()
if (currentFile != null && currentFile!!.exists()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can Kotlinify this a bit and fix a nullability warning in the code (the call is actually safe but the IDE doesn't know that):

currentFile?.takeIf { it.exists() }?.let { file ->
    ...
    val inputStream = FileInputStream(file)
    ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! addressed in e0f25fa (plus removed some commented code)

Also TIL:

the call is actually safe but the IDE doesn't know that

// val descriptor = assetManager!!.openFd("small.mp4")
val inputStream = FileInputStream(currentFile)
mediaPlayer = MediaPlayer().apply {
setDataSource(inputStream.getFD())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Kotlinification note, not a blocker)

This can use property access syntax: inputStream.fd.

// CameraX core library
def camerax_version = "1.0.0-alpha04"
// CameraX view library
def camerax_view_version = "1.0.0-alpha01"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one and the one below seems to be unused.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in 26767fc

permissions: Array<String>,
grantResults: IntArray
) {
if (!PermissionUtils.allRequiredPermissionsGranted(activity!!)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the !! force unwrap here by wrapping the whole function body in a null check:

activity?.let { activity ->
    ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up removing this entirely in bbcf4df, to match the same thing we did in e2fe8ee , related comment here: #22 (comment)

@aforcier aforcier changed the base branch from app/foundation-permissions-refactor to app/foundation August 13, 2019 19:03
@aforcier
Copy link
Collaborator

Awesome @mzorz, :shipit:!

@aforcier aforcier merged commit 447461f into app/foundation Aug 14, 2019
@aforcier aforcier deleted the app/foundation-camerax branch August 14, 2019 12:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants