From e1afcb2c385601ca9138ab2603f9373de397a340 Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Mon, 25 May 2026 21:36:07 -0300 Subject: [PATCH 1/8] Upgrade Gradle project --- Camera2SlowMotion/README.md | 4 +- Camera2SlowMotion/app/build.gradle | 46 +++++++++---------- .../app/src/main/AndroidManifest.xml | 5 +- Camera2SlowMotion/build.gradle | 6 +-- Camera2SlowMotion/gradle.properties | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 5 +- Camera2SlowMotion/settings.gradle | 4 +- Camera2SlowMotion/utils/build.gradle | 43 +++++++++-------- .../utils/src/main/AndroidManifest.xml | 2 +- 9 files changed, 59 insertions(+), 60 deletions(-) diff --git a/Camera2SlowMotion/README.md b/Camera2SlowMotion/README.md index f2fa3d4f..71a03757 100644 --- a/Camera2SlowMotion/README.md +++ b/Camera2SlowMotion/README.md @@ -22,8 +22,8 @@ in an MP4 video file. Pre-requisites -------------- -- Android SDK 29+ -- Android Studio 3.5+ +- Android SDK 33+ +- Android Studio Panda 4 - Device with high-speed capture capability Screenshots diff --git a/Camera2SlowMotion/app/build.gradle b/Camera2SlowMotion/app/build.gradle index 017c2ed9..703ba89d 100644 --- a/Camera2SlowMotion/app/build.gradle +++ b/Camera2SlowMotion/app/build.gradle @@ -15,17 +15,15 @@ */ apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' apply plugin: "androidx.navigation.safeargs" android { - compileSdkVersion 29 + compileSdkVersion 37 defaultConfig { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" applicationId "com.android.example.camera2.slowmo" - minSdkVersion 23 - targetSdkVersion 29 + minSdkVersion 33 + targetSdkVersion 37 versionCode 1 versionName "1.0.0" } @@ -35,14 +33,10 @@ android { targetCompatibility rootProject.ext.java_version } - kotlinOptions { - jvmTarget = "$rootProject.ext.java_version" - } - buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -59,37 +53,39 @@ android { buildFeatures { viewBinding true + buildConfig true } + namespace 'com.example.android.camera2.slowmo' } dependencies { implementation project(':utils') // Kotlin lang - implementation 'androidx.core:core-ktx:1.3.0' + implementation 'androidx.core:core-ktx:1.18.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0' // App compat and UI things - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.10.0' + implementation 'androidx.constraintlayout:constraintlayout:2.2.1' // Navigation library - def nav_version = "2.2.2" + def nav_version = "2.9.8" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" // Unit testing - testImplementation 'androidx.test.ext:junit:1.1.1' - testImplementation 'androidx.test:rules:1.2.0' - testImplementation 'androidx.test:runner:1.2.0' - testImplementation 'androidx.test.espresso:espresso-core:3.2.0' - testImplementation "org.robolectric:robolectric:4.3.1" + testImplementation 'androidx.test.ext:junit:1.3.0' + testImplementation 'androidx.test:rules:1.7.0' + testImplementation 'androidx.test:runner:1.7.0' + testImplementation 'androidx.test.espresso:espresso-core:3.7.0' + testImplementation "org.robolectric:robolectric:4.16.1" // Instrumented testing - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test.ext:junit:1.3.0' + androidTestImplementation 'androidx.test:rules:1.7.0' + androidTestImplementation 'androidx.test:runner:1.7.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' } diff --git a/Camera2SlowMotion/app/src/main/AndroidManifest.xml b/Camera2SlowMotion/app/src/main/AndroidManifest.xml index 571499b6..94cea4f4 100644 --- a/Camera2SlowMotion/app/src/main/AndroidManifest.xml +++ b/Camera2SlowMotion/app/src/main/AndroidManifest.xml @@ -15,8 +15,7 @@ ~ limitations under the License. --> + xmlns:tools="http://schemas.android.com/tools"> @@ -26,7 +25,6 @@ @@ -34,6 +32,7 @@ diff --git a/Camera2SlowMotion/build.gradle b/Camera2SlowMotion/build.gradle index 60be841e..229eb498 100644 --- a/Camera2SlowMotion/build.gradle +++ b/Camera2SlowMotion/build.gradle @@ -18,7 +18,7 @@ buildscript { // Top-level variables used for versioning - ext.kotlin_version = '1.5.21' + ext.kotlin_version = '2.2.20' ext.java_version = JavaVersion.VERSION_1_8 repositories { @@ -26,9 +26,9 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:9.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.4" + classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.9.8' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/Camera2SlowMotion/gradle.properties b/Camera2SlowMotion/gradle.properties index b448ae14..0e99b717 100644 --- a/Camera2SlowMotion/gradle.properties +++ b/Camera2SlowMotion/gradle.properties @@ -24,5 +24,7 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -android.enableJetifier=true android.useAndroidX=true +android.nonTransitiveRClass=false +android.uniquePackageNames=false +android.r8.strictFullModeForKeepRules=false diff --git a/Camera2SlowMotion/gradle/wrapper/gradle-wrapper.properties b/Camera2SlowMotion/gradle/wrapper/gradle-wrapper.properties index 9bc83a98..4e2ef5c1 100644 --- a/Camera2SlowMotion/gradle/wrapper/gradle-wrapper.properties +++ b/Camera2SlowMotion/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Wed Mar 31 20:47:06 PDT 2021 +#Mon May 25 20:32:25 BRT 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip diff --git a/Camera2SlowMotion/settings.gradle b/Camera2SlowMotion/settings.gradle index 663c0482..460a98c5 100644 --- a/Camera2SlowMotion/settings.gradle +++ b/Camera2SlowMotion/settings.gradle @@ -1,4 +1,6 @@ -/* +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +}/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Camera2SlowMotion/utils/build.gradle b/Camera2SlowMotion/utils/build.gradle index 1ef65fa3..3d038586 100644 --- a/Camera2SlowMotion/utils/build.gradle +++ b/Camera2SlowMotion/utils/build.gradle @@ -15,16 +15,12 @@ */ apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' android { - compileSdkVersion 29 + compileSdkVersion 37 defaultConfig { minSdkVersion 21 - targetSdkVersion 29 - versionCode 1 - versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' @@ -35,41 +31,44 @@ android { targetCompatibility rootProject.ext.java_version } - kotlinOptions { - jvmTarget = "$rootProject.ext.java_version" - } - buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + namespace 'com.example.android.camera.utils' + lint { + targetSdk 29 + } + testOptions { + targetSdk 29 + } } dependencies { // Kotlin lang implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0' // App compat and UI things - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.appcompat:appcompat:1.7.1' + implementation 'androidx.recyclerview:recyclerview:1.4.0' // EXIF Interface - implementation 'androidx.exifinterface:exifinterface:1.2.0' + implementation 'androidx.exifinterface:exifinterface:1.4.2' // Unit testing - testImplementation 'androidx.test.ext:junit:1.1.1' - testImplementation 'androidx.test:rules:1.2.0' - testImplementation 'androidx.test:runner:1.2.0' - testImplementation 'androidx.test.espresso:espresso-core:3.2.0' - testImplementation 'org.robolectric:robolectric:4.3.1' + testImplementation 'androidx.test.ext:junit:1.3.0' + testImplementation 'androidx.test:rules:1.7.0' + testImplementation 'androidx.test:runner:1.7.0' + testImplementation 'androidx.test.espresso:espresso-core:3.7.0' + testImplementation 'org.robolectric:robolectric:4.16.1' // Instrumented testing - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test.ext:junit:1.3.0' + androidTestImplementation 'androidx.test:rules:1.7.0' + androidTestImplementation 'androidx.test:runner:1.7.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' } diff --git a/Camera2SlowMotion/utils/src/main/AndroidManifest.xml b/Camera2SlowMotion/utils/src/main/AndroidManifest.xml index 2e13c37a..b708cb2c 100644 --- a/Camera2SlowMotion/utils/src/main/AndroidManifest.xml +++ b/Camera2SlowMotion/utils/src/main/AndroidManifest.xml @@ -14,4 +14,4 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + From 254be59d9fd032b768ae92aa897b2b7fde10043f Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Mon, 25 May 2026 21:44:57 -0300 Subject: [PATCH 2/8] Upgrade CameraActivity.kt - Replace elements deprecated in Java --- .../android/camera2/slowmo/CameraActivity.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt index 5a56941c..2e6c5d92 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt @@ -19,6 +19,8 @@ package com.example.android.camera2.slowmo import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat import com.example.android.camera2.slowmo.databinding.ActivityCameraBinding class CameraActivity : AppCompatActivity() { @@ -33,21 +35,16 @@ class CameraActivity : AppCompatActivity() { override fun onResume() { super.onResume() - // Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may + // Before setting full screen, 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 activityCameraBinding.fragmentContainer.postDelayed({ - activityCameraBinding.fragmentContainer.systemUiVisibility = FLAGS_FULLSCREEN + WindowCompat + .getInsetsController(window, window.decorView) + .hide(WindowInsetsCompat.Type.systemBars()) }, IMMERSIVE_FLAG_TIMEOUT) } companion object { - /** Combination of all flags required to put activity into immersive mode */ - const val FLAGS_FULLSCREEN = - 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 - /** Milliseconds used for UI animations */ const val ANIMATION_FAST_MILLIS = 50L const val ANIMATION_SLOW_MILLIS = 100L From 2dcb2499f02df6326b1eab393327ffc24461ac0b Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Mon, 25 May 2026 21:59:06 -0300 Subject: [PATCH 3/8] Upgrade PermissionsFragment.kt - Replace elements deprecated in Java --- .../slowmo/fragments/PermissionsFragment.kt | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt index 220f8896..26605d02 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt @@ -21,46 +21,47 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Bundle import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment -import androidx.navigation.Navigation -import com.example.android.camera2.slowmo.R +import androidx.navigation.fragment.findNavController -private const val PERMISSIONS_REQUEST_CODE = 10 private val PERMISSIONS_REQUIRED = arrayOf( - Manifest.permission.CAMERA, - Manifest.permission.RECORD_AUDIO) + Manifest.permission.CAMERA, + Manifest.permission.RECORD_AUDIO +) /** * This [Fragment] requests permissions and, once granted, it will navigate to the next fragment */ class PermissionsFragment : Fragment() { + private val requestPermissionsLauncher = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissions -> + val allGranted = permissions.values.all { it } + + if (allGranted) { + navigateToSelectorFragment() + } else { + Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show() + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (hasPermissions(requireContext())) { // If permissions have already been granted, proceed - Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate( - PermissionsFragmentDirections.actionPermissionsToSelector()) + navigateToSelectorFragment() } else { // Request camera-related permissions - requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE) + requestPermissionsLauncher.launch(PERMISSIONS_REQUIRED) } } - override fun onRequestPermissionsResult( - requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == PERMISSIONS_REQUEST_CODE) { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - // Takes the user to the success fragment when permission is granted - Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate( - PermissionsFragmentDirections.actionPermissionsToSelector()) - } else { - Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show() - } - } + fun navigateToSelectorFragment() { + findNavController().navigate(PermissionsFragmentDirections.actionPermissionsToSelector()) } companion object { From 784bac8029bff10f2163ae975b6e821bef87be5b Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Tue, 26 May 2026 11:42:17 -0300 Subject: [PATCH 4/8] Upgrade SelectorFragment.kt --- .../android/camera2/slowmo/fragments/SelectorFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt index 580a714b..a240dad4 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt @@ -32,6 +32,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.android.camera.utils.GenericListAdapter import com.example.android.camera2.slowmo.R +import androidx.navigation.findNavController /** * In this [Fragment] we let users pick a camera, size and FPS to use for high @@ -43,7 +44,7 @@ class SelectorFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? = RecyclerView(requireContext()) + ): View = RecyclerView(requireContext()) @SuppressLint("MissingPermission") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -61,8 +62,7 @@ class SelectorFragment : Fragment() { adapter = GenericListAdapter(cameraList, itemLayoutId = layoutId) { view, item, _ -> view.findViewById(android.R.id.text1).text = item.title view.setOnClickListener { - Navigation.findNavController(requireActivity(), R.id.fragment_container) - .navigate(SelectorFragmentDirections.actionSelectorToCamera( + findNavController().navigate(SelectorFragmentDirections.actionSelectorToCamera( item.cameraId, item.size.width, item.size.height, item.fps)) } } From 226e4bec0b149f1b5a49679df017c2e70d364df6 Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Tue, 26 May 2026 11:58:36 -0300 Subject: [PATCH 5/8] Upgrade CameraFragment.kt --- .../slowmo/fragments/CameraFragment.kt | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt index 547f5c8a..456115ed 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt @@ -27,6 +27,8 @@ import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession import android.hardware.camera2.CameraDevice import android.hardware.camera2.CameraManager import android.hardware.camera2.CaptureRequest +import android.hardware.camera2.params.OutputConfiguration +import android.hardware.camera2.params.SessionConfiguration import android.hardware.camera2.params.StreamConfigurationMap import android.media.MediaCodec import android.media.MediaRecorder @@ -37,7 +39,13 @@ import android.os.HandlerThread import android.util.Log import android.util.Range import android.util.Size -import android.view.* +import android.view.Display +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.Surface +import android.view.SurfaceHolder +import android.view.View +import android.view.ViewGroup import android.webkit.MimeTypeMap import androidx.core.content.FileProvider import androidx.core.graphics.drawable.toDrawable @@ -45,7 +53,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController -import androidx.navigation.Navigation +import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import com.example.android.camera.utils.OrientationLiveData import com.example.android.camera.utils.SIZE_1080P @@ -56,6 +64,7 @@ import com.example.android.camera2.slowmo.CameraActivity import com.example.android.camera2.slowmo.R import com.example.android.camera2.slowmo.databinding.FragmentCameraBinding import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine @@ -63,10 +72,9 @@ import java.io.File import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import kotlin.RuntimeException +import java.util.concurrent.Executor import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine class CameraFragment : Fragment() { @@ -80,7 +88,7 @@ class CameraFragment : Fragment() { /** Host's navigation controller */ private val navController: NavController by lazy { - Navigation.findNavController(requireActivity(), R.id.fragment_container) + requireActivity().findNavController(R.id.fragment_container) } /** Detects, characterizes, and connects to a CameraDevice (used for all camera operations) */ @@ -226,7 +234,7 @@ class CameraFragment : Fragment() { } /** Creates a [MediaRecorder] instance using the provided [Surface] as input */ - private fun createRecorder(surface: Surface) = MediaRecorder().apply { + private fun createRecorder(surface: Surface) = MediaRecorder(requireContext()).apply { setAudioSource(MediaRecorder.AudioSource.MIC) setVideoSource(MediaRecorder.VideoSource.SURFACE) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) @@ -288,6 +296,7 @@ class CameraFragment : Fragment() { */ @SuppressLint("ClickableViewAccessibility") private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) { + val executor = Dispatchers.Default.asExecutor() // Open the selected camera camera = openCamera(cameraManager, args.cameraId, cameraHandler) @@ -296,7 +305,7 @@ class CameraFragment : Fragment() { val targets = listOf(fragmentCameraBinding.viewFinder.holder.surface, recorderSurface) // Start a capture session using our open camera and list of Surfaces where frames will go - session = createCaptureSession(camera, targets, cameraHandler) + session = createCaptureSession(camera, targets, executor) // Ensures the requested size and FPS are compatible with this camera val fpsRange = Range(args.fps, args.fps) @@ -358,14 +367,14 @@ class CameraFragment : Fragment() { view.context, arrayOf(outputFile.absolutePath), null, null) // Launch external activity via intent to play video recorded using our provider - startActivity(Intent().apply { - action = Intent.ACTION_VIEW - type = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(outputFile.extension) + startActivity(Intent(Intent.ACTION_VIEW).apply { val authority = "${BuildConfig.APPLICATION_ID}.provider" - data = FileProvider.getUriForFile(view.context, authority, outputFile) - flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_ACTIVITY_CLEAR_TOP + setDataAndType( + FileProvider.getUriForFile(view.context, authority, outputFile), + MimeTypeMap.getSingleton().getMimeTypeFromExtension(outputFile.extension) + ) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) }) // Finishes our current camera screen @@ -416,23 +425,28 @@ class CameraFragment : Fragment() { private suspend fun createCaptureSession( device: CameraDevice, targets: List, - handler: Handler? = null - ): CameraConstrainedHighSpeedCaptureSession = suspendCoroutine { cont -> + executor: Executor + ): CameraConstrainedHighSpeedCaptureSession = suspendCancellableCoroutine { cont -> // Creates a capture session using the predefined targets, and defines a session state // callback which resumes the coroutine once the session is configured - device.createConstrainedHighSpeedCaptureSession( - targets, object: CameraCaptureSession.StateCallback() { - - override fun onConfigured(session: CameraCaptureSession) = - cont.resume(session as CameraConstrainedHighSpeedCaptureSession) - - override fun onConfigureFailed(session: CameraCaptureSession) { - val exc = RuntimeException("Camera ${device.id} session configuration failed") - Log.e(TAG, exc.message, exc) - cont.resumeWithException(exc) - } - }, handler) + device.createCaptureSession( + SessionConfiguration( + SessionConfiguration.SESSION_HIGH_SPEED, + targets.map(::OutputConfiguration), + executor, + object : CameraCaptureSession.StateCallback() { + override fun onConfigured(session: CameraCaptureSession) = + cont.resume(session as CameraConstrainedHighSpeedCaptureSession) + + override fun onConfigureFailed(session: CameraCaptureSession) { + val exc = RuntimeException("Camera ${device.id} session configuration failed") + Log.e(TAG, exc.message, exc) + cont.resumeWithException(exc) + } + } + ) + ) } override fun onStop() { From 9e8dff2d47d9864769be7974f1011aa618f0be70 Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Tue, 26 May 2026 13:34:34 -0300 Subject: [PATCH 6/8] Minor fixes --- .../android/camera2/slowmo/CameraActivity.kt | 2 - .../slowmo/fragments/CameraFragment.kt | 121 +++++++++--------- .../slowmo/fragments/PermissionsFragment.kt | 6 +- .../slowmo/fragments/SelectorFragment.kt | 62 +++++---- .../app/src/main/res/navigation/nav_graph.xml | 4 +- 5 files changed, 98 insertions(+), 97 deletions(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt index 2e6c5d92..ab45b501 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/CameraActivity.kt @@ -17,14 +17,12 @@ package com.example.android.camera2.slowmo import android.os.Bundle -import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import com.example.android.camera2.slowmo.databinding.ActivityCameraBinding class CameraActivity : AppCompatActivity() { - private lateinit var activityCameraBinding: ActivityCameraBinding override fun onCreate(savedInstanceState: Bundle?) { diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt index 456115ed..76cf1038 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/CameraFragment.kt @@ -52,8 +52,6 @@ import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope -import androidx.navigation.NavController -import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import com.example.android.camera.utils.OrientationLiveData import com.example.android.camera.utils.SIZE_1080P @@ -61,10 +59,8 @@ import com.example.android.camera.utils.SmartSize import com.example.android.camera.utils.getDisplaySmartSize import com.example.android.camera2.slowmo.BuildConfig import com.example.android.camera2.slowmo.CameraActivity -import com.example.android.camera2.slowmo.R import com.example.android.camera2.slowmo.databinding.FragmentCameraBinding import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asExecutor import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine @@ -72,12 +68,14 @@ import java.io.File import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import java.util.concurrent.Executor +import kotlin.RuntimeException import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException +import androidx.navigation.fragment.findNavController +import kotlinx.coroutines.asExecutor +import java.util.concurrent.Executor class CameraFragment : Fragment() { - /** Android ViewBinding */ private var _fragmentCameraBinding: FragmentCameraBinding? = null @@ -86,11 +84,6 @@ class CameraFragment : Fragment() { /** AndroidX navigation arguments */ private val args: CameraFragmentArgs by navArgs() - /** Host's navigation controller */ - private val navController: NavController by lazy { - requireActivity().findNavController(R.id.fragment_container) - } - /** Detects, characterizes, and connects to a CameraDevice (used for all camera operations) */ private val cameraManager: CameraManager by lazy { val context = requireContext().applicationContext @@ -103,14 +96,13 @@ class CameraFragment : Fragment() { } /** File where the recording will be saved */ - private val outputFile: File by lazy { createFile(requireContext(), "mp4") } + private val outputFile: File by lazy { createFile(requireContext()) } /** - * Setup a persistent [Surface] for the recorder so we can use it as an output target for the + * Set up a persistent [Surface] for the recorder so we can use it as an output target for the * camera session without preparing the recorder */ private val recorderSurface: Surface by lazy { - // Get a persistent Surface from MediaCodec, don't forget to release when done val surface = MediaCodec.createPersistentInputSurface() @@ -144,7 +136,10 @@ class CameraFragment : Fragment() { // Remove white flash animation fragmentCameraBinding.overlay.foreground = null // Restart animation recursively - fragmentCameraBinding.overlay.postDelayed(animationTask, CameraActivity.ANIMATION_FAST_MILLIS) + fragmentCameraBinding.overlay.postDelayed( + animationTask, + CameraActivity.ANIMATION_FAST_MILLIS + ) }, CameraActivity.ANIMATION_FAST_MILLIS) } } @@ -206,19 +201,28 @@ class CameraFragment : Fragment() { fragmentCameraBinding.viewFinder.holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceDestroyed(holder: SurfaceHolder) = Unit override fun surfaceChanged( - holder: SurfaceHolder, - format: Int, - width: Int, - height: Int) = Unit + holder: SurfaceHolder, + format: Int, + width: Int, + height: Int + ) = Unit override fun surfaceCreated(holder: SurfaceHolder) { - // Selects appropriate preview size and configures view finder val previewSize = getConstrainedPreviewOutputSize( - fragmentCameraBinding.viewFinder.display, characteristics, SurfaceHolder::class.java) - Log.d(TAG, "View finder size: ${fragmentCameraBinding.viewFinder.width} x ${fragmentCameraBinding.viewFinder.height}") + fragmentCameraBinding.viewFinder.display, + characteristics, + SurfaceHolder::class.java + ) + Log.d( + TAG, + "View finder size: ${fragmentCameraBinding.viewFinder.width} x ${fragmentCameraBinding.viewFinder.height}" + ) Log.d(TAG, "Selected preview size: $previewSize") - fragmentCameraBinding.viewFinder.setAspectRatio(previewSize.width, previewSize.height) + fragmentCameraBinding.viewFinder.setAspectRatio( + previewSize.width, + previewSize.height + ) // To ensure that size is set, initialize camera in the view's thread fragmentCameraBinding.viewFinder.post { initializeCamera() } @@ -227,8 +231,8 @@ class CameraFragment : Fragment() { // Used to rotate the output media to match device orientation relativeOrientation = OrientationLiveData(requireContext(), characteristics).apply { - observe(viewLifecycleOwner, Observer { - orientation -> Log.d(TAG, "Orientation changed: $orientation") + observe(viewLifecycleOwner, Observer { orientation -> + Log.d(TAG, "Orientation changed: $orientation") }) } } @@ -252,21 +256,19 @@ class CameraFragment : Fragment() { * additional constraint that the selected size must also be available as one of possible * constrained high-speed session sizes. */ - private fun getConstrainedPreviewOutputSize( - display: Display, - characteristics: CameraCharacteristics, - targetClass: Class, - format: Int? = null + private fun getConstrainedPreviewOutputSize( + display: Display, + characteristics: CameraCharacteristics, + targetClass: Class, + format: Int? = null ): Size { - // Find which is smaller: screen or 1080p val screenSize = getDisplaySmartSize(display) val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short val maxSize = if (hdScreen) SIZE_1080P else screenSize // If image format is provided, use it to determine supported sizes; else use target class - val config = characteristics.get( - CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! + val config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! if (format == null) { assert(StreamConfigurationMap.isOutputSupportedFor(targetClass)) } else { @@ -281,13 +283,14 @@ class CameraFragment : Fragment() { // Filter sizes which are part of the high speed constrained session val validSizes = allSizes - .filter { highSpeedSizes.contains(it) } - .sortedWith(compareBy { it.height * it.width }) - .map { SmartSize(it.width, it.height) }.reversed() + .filter { highSpeedSizes.contains(it) } + .sortedWith(compareBy { it.height * it.width }) + .map { SmartSize(it.width, it.height) }.reversed() // Then, get the largest output size that is smaller or equal than our max size return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size } + /** * Begin all camera operations in a coroutine in the main thread. This function: * - Opens the camera @@ -296,21 +299,23 @@ class CameraFragment : Fragment() { */ @SuppressLint("ClickableViewAccessibility") private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) { - val executor = Dispatchers.Default.asExecutor() - // Open the selected camera camera = openCamera(cameraManager, args.cameraId, cameraHandler) // Creates list of Surfaces where the camera will output frames val targets = listOf(fragmentCameraBinding.viewFinder.holder.surface, recorderSurface) + val executor = Dispatchers.Default.asExecutor() + // Start a capture session using our open camera and list of Surfaces where frames will go session = createCaptureSession(camera, targets, executor) // Ensures the requested size and FPS are compatible with this camera val fpsRange = Range(args.fps, args.fps) - assert(true == characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) - ?.getHighSpeedVideoFpsRangesFor(Size(args.width, args.height))?.contains(fpsRange)) + assert( + true == characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) + ?.getHighSpeedVideoFpsRangesFor(Size(args.width, args.height))?.contains(fpsRange) + ) // Sends the capture request as frequently as possible until the session is torn down or // session.stopRepeating() is called @@ -319,12 +324,9 @@ class CameraFragment : Fragment() { // Listen to the capture button fragmentCameraBinding.captureButton.setOnTouchListener { view, event -> when (event.action) { - MotionEvent.ACTION_DOWN -> lifecycleScope.launch(Dispatchers.IO) { - // Prevents screen rotation during the video recording - requireActivity().requestedOrientation = - ActivityInfo.SCREEN_ORIENTATION_LOCKED + requireActivity().requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED // Stops preview requests, and start record requests session.stopRepeating() @@ -345,10 +347,8 @@ class CameraFragment : Fragment() { } MotionEvent.ACTION_UP -> lifecycleScope.launch(Dispatchers.IO) { - // Unlocks screen rotation after recording finished - requireActivity().requestedOrientation = - ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + requireActivity().requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED // Requires recording of at least MIN_REQUIRED_RECORDING_TIME_MILLIS val elapsedTimeMillis = System.currentTimeMillis() - recordingStartMillis @@ -364,7 +364,8 @@ class CameraFragment : Fragment() { // Broadcasts the media file to the rest of the system MediaScannerConnection.scanFile( - view.context, arrayOf(outputFile.absolutePath), null, null) + view.context, arrayOf(outputFile.absolutePath), null, null + ) // Launch external activity via intent to play video recorded using our provider startActivity(Intent(Intent.ACTION_VIEW).apply { @@ -379,7 +380,7 @@ class CameraFragment : Fragment() { // Finishes our current camera screen delay(CameraActivity.ANIMATION_SLOW_MILLIS) - navController.popBackStack() + findNavController().popBackStack() } } @@ -390,9 +391,9 @@ class CameraFragment : Fragment() { /** Opens the camera and returns the opened device (as the result of the suspend coroutine) */ @SuppressLint("MissingPermission") private suspend fun openCamera( - manager: CameraManager, - cameraId: String, - handler: Handler? = null + manager: CameraManager, + cameraId: String, + handler: Handler? = null ): CameraDevice = suspendCancellableCoroutine { cont -> manager.openCamera(cameraId, object : CameraDevice.StateCallback() { override fun onOpened(device: CameraDevice) = cont.resume(device) @@ -403,7 +404,7 @@ class CameraFragment : Fragment() { } override fun onError(device: CameraDevice, error: Int) { - val msg = when(error) { + val msg = when (error) { ERROR_CAMERA_DEVICE -> "Fatal (device)" ERROR_CAMERA_DISABLED -> "Device policy" ERROR_CAMERA_IN_USE -> "Camera in use" @@ -423,11 +424,10 @@ class CameraFragment : Fragment() { * suspend coroutine) */ private suspend fun createCaptureSession( - device: CameraDevice, - targets: List, - executor: Executor + device: CameraDevice, + targets: List, + executor: Executor ): CameraConstrainedHighSpeedCaptureSession = suspendCancellableCoroutine { cont -> - // Creates a capture session using the predefined targets, and defines a session state // callback which resumes the coroutine once the session is configured device.createCaptureSession( @@ -442,7 +442,8 @@ class CameraFragment : Fragment() { override fun onConfigureFailed(session: CameraCaptureSession) { val exc = RuntimeException("Camera ${device.id} session configuration failed") Log.e(TAG, exc.message, exc) - cont.resumeWithException(exc) + if (cont.isActive) + cont.resumeWithException(exc) } } ) @@ -483,9 +484,9 @@ class CameraFragment : Fragment() { private const val FPS_PREVIEW_ONLY: Int = 30 /** Creates a [File] named with the current date and time */ - private fun createFile(context: Context, extension: String): File { + private fun createFile(context: Context): File { val sdf = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US) - return File(context.filesDir, "VID_${sdf.format(Date())}.$extension") + return File(context.filesDir, "VID_${sdf.format(Date())}.mp4") } } } diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt index 26605d02..7c08338f 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt @@ -35,13 +35,10 @@ private val PERMISSIONS_REQUIRED = arrayOf( * This [Fragment] requests permissions and, once granted, it will navigate to the next fragment */ class PermissionsFragment : Fragment() { - private val requestPermissionsLauncher = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> - val allGranted = permissions.values.all { it } - - if (allGranted) { + if (permissions.values.all { it }) { navigateToSelectorFragment() } else { Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show() @@ -65,7 +62,6 @@ class PermissionsFragment : Fragment() { } companion object { - /** Convenience method used to check if all permissions required by this app are granted */ fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt index a240dad4..8e229cff 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt @@ -27,11 +27,9 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.fragment.app.Fragment -import androidx.navigation.Navigation import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.android.camera.utils.GenericListAdapter -import com.example.android.camera2.slowmo.R import androidx.navigation.findNavController /** @@ -39,11 +37,10 @@ import androidx.navigation.findNavController * speed video recording */ class SelectorFragment : Fragment() { - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View = RecyclerView(requireContext()) @SuppressLint("MissingPermission") @@ -53,33 +50,40 @@ class SelectorFragment : Fragment() { view.apply { layoutManager = LinearLayoutManager(requireContext()) - val cameraManager = - requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager + val cameraManager = requireContext() + .getSystemService(Context.CAMERA_SERVICE) as CameraManager val cameraList = enumerateHighSpeedCameras(cameraManager) - val layoutId = android.R.layout.simple_list_item_1 - adapter = GenericListAdapter(cameraList, itemLayoutId = layoutId) { view, item, _ -> + adapter = GenericListAdapter( + cameraList, + android.R.layout.simple_list_item_1 + ) { view, item, _ -> view.findViewById(android.R.id.text1).text = item.title view.setOnClickListener { - findNavController().navigate(SelectorFragmentDirections.actionSelectorToCamera( - item.cameraId, item.size.width, item.size.height, item.fps)) + findNavController().navigate( + SelectorFragmentDirections.actionSelectorToCamera( + item.cameraId, + item.size.width, + item.size.height, + item.fps + ) + ) } } } - } companion object { - private data class CameraInfo( - val title: String, - val cameraId: String, - val size: Size, - val fps: Int) + val title: String, + val cameraId: String, + val size: Size, + val fps: Int + ) /** Converts a lens orientation enum into a human-readable string */ - private fun lensOrientationString(value: Int) = when(value) { + private fun lensOrientationString(value: Int) = when (value) { CameraCharacteristics.LENS_FACING_BACK -> "Back" CameraCharacteristics.LENS_FACING_FRONT -> "Front" CameraCharacteristics.LENS_FACING_EXTERNAL -> "External" @@ -94,28 +98,30 @@ class SelectorFragment : Fragment() { // Iterate over the list of cameras and add those with high speed video recording // capability to our output. This function only returns those cameras that declare // constrained high speed video recording, but some cameras may be capable of doing - // unconstrained video recording with high enough FPS for some use cases and they will + // unconstrained video recording with high enough FPS for some use cases, and they will // not necessarily declare constrained high speed video capability. cameraManager.cameraIdList.forEach { id -> val characteristics = cameraManager.getCameraCharacteristics(id) val orientation = lensOrientationString( - characteristics.get(CameraCharacteristics.LENS_FACING)!!) + characteristics.get(CameraCharacteristics.LENS_FACING)!! + ) // Query the available capabilities and output formats - val capabilities = characteristics.get( - CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!! - val cameraConfig = characteristics.get( - CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! + val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!! + val cameraConfig = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! // Return cameras that support constrained high video capability - if (capabilities.contains(CameraCharacteristics - .REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO)) { + if (capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO)) { // For each camera, list its compatible sizes and FPS ranges cameraConfig.highSpeedVideoSizes.forEach { size -> cameraConfig.getHighSpeedVideoFpsRangesFor(size).forEach { fpsRange -> val fps = fpsRange.upper val info = CameraInfo( - "$orientation ($id) $size $fps FPS", id, size, fps) + "$orientation ($id) $size $fps FPS", + id, + size, + fps + ) // Only report the highest FPS in the range, avoid duplicates if (!availableCameras.contains(info)) availableCameras.add(info) diff --git a/Camera2SlowMotion/app/src/main/res/navigation/nav_graph.xml b/Camera2SlowMotion/app/src/main/res/navigation/nav_graph.xml index 44cdd555..227a4280 100644 --- a/Camera2SlowMotion/app/src/main/res/navigation/nav_graph.xml +++ b/Camera2SlowMotion/app/src/main/res/navigation/nav_graph.xml @@ -68,8 +68,8 @@ app:argType="integer" /> From 399fe402d04d56aca5c9a34ffa10acf2c84971f1 Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Tue, 26 May 2026 13:59:36 -0300 Subject: [PATCH 7/8] Fix findNavController Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../android/camera2/slowmo/fragments/SelectorFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt index 8e229cff..d70d8c43 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/SelectorFragment.kt @@ -30,7 +30,7 @@ import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.android.camera.utils.GenericListAdapter -import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController /** * In this [Fragment] we let users pick a camera, size and FPS to use for high From 62dd24d4777b3c7571f5f3f9470c98751064b94d Mon Sep 17 00:00:00 2001 From: Rodrigo Mologni Date: Tue, 26 May 2026 14:02:58 -0300 Subject: [PATCH 8/8] Fix navigateToSelectorFragment --- .../android/camera2/slowmo/fragments/PermissionsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt index 7c08338f..a9cf7ff4 100644 --- a/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt +++ b/Camera2SlowMotion/app/src/main/java/com/example/android/camera2/slowmo/fragments/PermissionsFragment.kt @@ -57,7 +57,7 @@ class PermissionsFragment : Fragment() { } } - fun navigateToSelectorFragment() { + private fun navigateToSelectorFragment() { findNavController().navigate(PermissionsFragmentDirections.actionPermissionsToSelector()) }