diff --git a/CameraXExtensions/app/build.gradle b/CameraXExtensions/app/build.gradle index 1a07b333..23f27ee4 100644 --- a/CameraXExtensions/app/build.gradle +++ b/CameraXExtensions/app/build.gradle @@ -32,7 +32,7 @@ android { defaultConfig { applicationId "com.example.android.cameraxextensions" minSdk 24 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0.0" @@ -67,7 +67,7 @@ android { dependencies { // Kotlin lang - implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.core:core-ktx:1.13.1' // CameraX implementation "androidx.camera:camera-core:$camerax_version" @@ -88,25 +88,27 @@ dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" // Image loading - implementation "io.coil-kt:coil:2.1.0" + implementation "io.coil-kt:coil:2.4.0" + + // Material Components + implementation 'com.google.android.material:material:1.12.0' // Compose - implementation 'androidx.compose.material:material:1.2.1' - implementation 'androidx.compose.ui:ui:1.2.1' - implementation 'androidx.compose.ui:ui-tooling-preview:1.2.1' - debugImplementation 'androidx.compose.ui:ui-tooling:1.2.1' - implementation 'androidx.activity:activity-compose:1.6.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1' - - implementation 'androidx.activity:activity-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.5.1' - implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.compose.material:material:1.6.7' + implementation 'androidx.compose.ui:ui:1.6.7' + implementation 'androidx.compose.ui:ui-tooling-preview:1.6.7' + debugImplementation 'androidx.compose.ui:ui-tooling:1.6.7' + implementation 'androidx.activity:activity-compose:1.9.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0' + + implementation 'androidx.activity:activity-ktx:1.9.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation "androidx.recyclerview:recyclerview:1.2.1" - + implementation "androidx.recyclerview:recyclerview:1.3.2" // Test testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt index 45394592..ec3522e7 100644 --- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt +++ b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt @@ -18,13 +18,19 @@ package com.example.android.cameraxextensions import android.Manifest import android.content.pm.PackageManager +import android.net.Uri import android.os.Bundle +import android.util.Log import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.camera.extensions.ExtensionMode import androidx.core.app.ActivityCompat -import androidx.lifecycle.* +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.example.android.cameraxextensions.adapter.CameraExtensionItem import com.example.android.cameraxextensions.model.CameraState import com.example.android.cameraxextensions.model.CameraUiAction @@ -76,6 +82,28 @@ class MainActivity : AppCompatActivity() { // monitors changes in camera permission state private lateinit var permissionState: MutableStateFlow + private var captureUri: Uri? = null + private var progressComplete: Boolean = false + + private suspend fun showCapture() { + if (captureUri == null || !progressComplete) return + + cameraExtensionsViewModel.stopPreview() + captureScreenViewState.emit( + captureScreenViewState.value + .updatePostCaptureScreen { + captureUri?.let { + PostCaptureScreenViewState.PostCaptureScreenVisibleViewState(it) + } ?: PostCaptureScreenViewState.PostCaptureScreenHiddenViewState + }.updateCameraScreen { + it.hideCameraControls() + .hideProcessProgressViewState() + } + ) + captureUri = null + progressComplete = false + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -145,6 +173,10 @@ class MainActivity : AppCompatActivity() { CameraUiAction.RequestPermissionClick -> { requestPermissionsLauncher.launch(Manifest.permission.CAMERA) } + CameraUiAction.ProcessProgressComplete -> { + progressComplete = true + showCapture() + } is CameraUiAction.Focus -> { cameraExtensionsViewModel.focus(action.meteringPoint) } @@ -169,10 +201,13 @@ class MainActivity : AppCompatActivity() { .updateCameraScreen { it.enableCameraShutter(true) .enableSwitchLens(true) + .hidePostview() } ) } CaptureState.CaptureReady -> { + captureUri = null + progressComplete = false captureScreenViewState.emit( captureScreenViewState.value .updateCameraScreen { @@ -191,23 +226,11 @@ class MainActivity : AppCompatActivity() { ) } is CaptureState.CaptureFinished -> { - cameraExtensionsViewModel.stopPreview() - captureScreenViewState.emit( - captureScreenViewState.value - .updatePostCaptureScreen { - val uri = state.outputResults.savedUri - if (uri != null) { - PostCaptureScreenViewState.PostCaptureScreenVisibleViewState( - uri - ) - } else { - PostCaptureScreenViewState.PostCaptureScreenHiddenViewState - } - } - .updateCameraScreen { - it.hideCameraControls() - } - ) + captureUri = state.outputResults.savedUri + if (!state.isProcessProgressSupported) { + progressComplete = true + } + showCapture() } is CaptureState.CaptureFailed -> { cameraExtensionsScreen.showCaptureError("Couldn't take photo") @@ -220,6 +243,24 @@ class MainActivity : AppCompatActivity() { it.showCameraControls() .enableCameraShutter(true) .enableSwitchLens(true) + .hideProcessProgressViewState() + .hidePostview() + } + ) + } + is CaptureState.CapturePostview -> { + captureScreenViewState.emit( + captureScreenViewState.value + .updateCameraScreen { + it.showPostview(state.bitmap) + } + ) + } + is CaptureState.CaptureProcessProgress -> { + captureScreenViewState.emit( + captureScreenViewState.value + .updateCameraScreen { + it.showProcessProgressViewState(state.progress) } ) } @@ -259,6 +300,7 @@ class MainActivity : AppCompatActivity() { } .updateCameraScreen { it.showCameraControls() + .hidePostview() .enableCameraShutter(false) .enableSwitchLens(false) } @@ -299,6 +341,7 @@ class MainActivity : AppCompatActivity() { captureScreenViewState.value .updateCameraScreen { state -> state.showCameraControls() + state.hidePostview() } .updatePostCaptureScreen { PostCaptureScreenViewState.PostCaptureScreenHiddenViewState diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt index 5ba45715..13c9dfb1 100644 --- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt +++ b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt @@ -23,10 +23,11 @@ import androidx.camera.extensions.ExtensionMode * User initiated actions related to camera operations. */ sealed class CameraUiAction { - object RequestPermissionClick : CameraUiAction() - object SwitchCameraClick : CameraUiAction() - object ShutterButtonClick : CameraUiAction() - object ClosePhotoPreviewClick : CameraUiAction() + data object RequestPermissionClick : CameraUiAction() + data object SwitchCameraClick : CameraUiAction() + data object ShutterButtonClick : CameraUiAction() + data object ClosePhotoPreviewClick : CameraUiAction() + data object ProcessProgressComplete : CameraUiAction() data class SelectCameraExtension(@ExtensionMode.Mode val extension: Int) : CameraUiAction() data class Focus(val meteringPoint: MeteringPoint) : CameraUiAction() data class Scale(val scaleFactor: Float) : CameraUiAction() diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt index e07fb2b4..e86e09ce 100644 --- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt +++ b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt @@ -16,6 +16,7 @@ package com.example.android.cameraxextensions.model +import android.graphics.Bitmap import androidx.camera.core.CameraSelector.LENS_FACING_BACK import androidx.camera.core.CameraSelector.LensFacing import androidx.camera.core.ImageCapture @@ -74,10 +75,23 @@ sealed class CaptureState { */ object CaptureStarted : CaptureState() + /** + * Capture postview is ready + */ + data class CapturePostview(val bitmap: Bitmap): CaptureState() + + /** + * Capture process progress updated with the [progress] value + */ + data class CaptureProcessProgress(val progress: Int): CaptureState() + /** * Capture completed successfully. */ - data class CaptureFinished(val outputResults: ImageCapture.OutputFileResults) : CaptureState() + data class CaptureFinished( + val outputResults: ImageCapture.OutputFileResults, + val isProcessProgressSupported: Boolean + ) : CaptureState() /** * Capture failed with an error. diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt index 40fd2f78..0befc776 100644 --- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt +++ b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt @@ -18,8 +18,10 @@ package com.example.android.cameraxextensions.ui import android.animation.Animator import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.Context +import android.graphics.Bitmap import android.net.Uri import android.util.TypedValue import android.view.GestureDetector.SimpleOnGestureListener @@ -30,6 +32,7 @@ import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.camera.view.PreviewView +import androidx.core.animation.doOnEnd import androidx.core.view.GestureDetectorCompat import androidx.core.view.isVisible import androidx.dynamicanimation.animation.DynamicAnimation @@ -46,9 +49,11 @@ import com.example.android.cameraxextensions.model.CameraUiAction import com.example.android.cameraxextensions.viewstate.CameraPreviewScreenViewState import com.example.android.cameraxextensions.viewstate.CaptureScreenViewState import com.example.android.cameraxextensions.viewstate.PostCaptureScreenViewState +import com.google.android.material.progressindicator.CircularProgressIndicator import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch +import kotlin.math.max /** * Displays the camera preview and captured photo. @@ -63,6 +68,7 @@ class CameraExtensionsScreen(private val root: View) { private const val SPRING_STIFFNESS_ALPHA_OUT = 100f private const val SPRING_STIFFNESS = 800f private const val SPRING_DAMPING_RATIO = 0.35f + private const val MAX_PROGRESS_ANIM_DURATION_MS = 3000 } private val context: Context = root.context @@ -79,6 +85,11 @@ class CameraExtensionsScreen(private val root: View) { private val permissionsRationale: TextView = root.findViewById(R.id.permissionsRationale) private val permissionsRequestButton: TextView = root.findViewById(R.id.permissionsRequestButton) + private val photoPostview: ImageView = root.findViewById(R.id.photoPostview) + private val processProgressContainer: View = + root.findViewById(R.id.processProgressContainer) + private val processProgressIndicator: CircularProgressIndicator = + root.findViewById(R.id.processProgressIndicator) val previewView: PreviewView = root.findViewById(R.id.previewView) @@ -216,10 +227,50 @@ class CameraExtensionsScreen(private val root: View) { } } + private fun showPostview(bitmap: Bitmap) { + if (photoPostview.isVisible) return + photoPostview.isVisible = true + photoPostview.load(bitmap) { + crossfade(true) + crossfade(200) + } + } + + private fun hidePostview() { + photoPostview.isVisible = false + } + + private fun showProcessProgressIndicator(progress: Int) { + processProgressContainer.isVisible = true + if (progress == processProgressIndicator.progress) return + + ObjectAnimator.ofInt(processProgressIndicator, "progress", progress).apply { + val currentProgress = processProgressIndicator.progress + val progressStep = max(0, progress - currentProgress) + duration = (progressStep / 100f * MAX_PROGRESS_ANIM_DURATION_MS).toLong() + doOnEnd { + if (animatedValue == 100) { + root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch { + _action.emit(CameraUiAction.ProcessProgressComplete) + } + } + } + start() + } + } + + private fun hideProcessProgressIndicator() { + processProgressContainer.isVisible = false + processProgressIndicator.progress = 0 + } + private fun showPhoto(uri: Uri?) { if (uri == null) return photoPreview.isVisible = true - photoPreview.load(uri) + photoPreview.load(uri) { + crossfade(true) + crossfade(200) + } closePhotoPreview.isVisible = true } @@ -237,6 +288,18 @@ class CameraExtensionsScreen(private val root: View) { extensionSelector.isVisible = state.extensionsSelectorViewState.isVisible extensionsAdapter.submitList(state.extensionsSelectorViewState.extensions) + + if (state.postviewViewState.isVisible) { + showPostview(state.postviewViewState.bitmap!!) + } else { + hidePostview() + } + + if (state.processProgressViewState.isVisible) { + showProcessProgressIndicator(state.processProgressViewState.progress) + } else { + hideProcessProgressIndicator() + } } private fun onItemClick(view: View) { diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt index c53aa38f..12623ae9 100644 --- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt +++ b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt @@ -17,13 +17,21 @@ package com.example.android.cameraxextensions.viewmodel import android.app.Application -import androidx.camera.core.* +import android.graphics.Bitmap +import androidx.camera.core.AspectRatio +import androidx.camera.core.Camera +import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector.LensFacing +import androidx.camera.core.FocusMeteringAction +import androidx.camera.core.ImageCapture +import androidx.camera.core.ImageCaptureException +import androidx.camera.core.MeteringPoint +import androidx.camera.core.Preview +import androidx.camera.core.UseCaseGroup import androidx.camera.extensions.ExtensionMode import androidx.camera.extensions.ExtensionsManager import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.view.PreviewView -import androidx.compose.ui.layout.ScaleFactor import androidx.core.net.toUri import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel @@ -58,7 +66,7 @@ class CameraExtensionsViewModel( private var camera: Camera? = null - private val imageCapture = ImageCapture.Builder() + private var imageCapture = ImageCapture.Builder() .setTargetAspectRatio(AspectRatio.RATIO_16_9) .build() @@ -142,6 +150,19 @@ class CameraExtensionsViewModel( currentCameraUiState.extensionMode ) } + + cameraProvider.unbindAll() + camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector) + + camera?.cameraInfo?.let { + val isPostviewSupported = + ImageCapture.getImageCaptureCapabilities(it).isPostviewSupported + imageCapture = ImageCapture.Builder() + .setTargetAspectRatio(AspectRatio.RATIO_16_9) + .setPostviewEnabled(isPostviewSupported) + .build() + } + val useCaseGroup = UseCaseGroup.Builder() .setViewPort(previewView.viewPort!!) .addUseCase(imageCapture) @@ -223,6 +244,14 @@ class CameraExtensionsViewModel( .setMetadata(metadata) .build() + camera?.cameraInfo?.let { + if (ImageCapture.getImageCaptureCapabilities(it).isCaptureProcessProgressSupported) { + viewModelScope.launch { + _captureUiState.emit(CaptureState.CaptureProcessProgress(0)) + } + } + } + imageCapture.takePicture( outputFileOptions, Dispatchers.Default.asExecutor(), @@ -232,8 +261,19 @@ class CameraExtensionsViewModel( application, outputFileResults.savedUri ?: photoFile.toUri() ) + val isProcessProgressSupported = camera?.cameraInfo?.let { + ImageCapture.getImageCaptureCapabilities(it).isCaptureProcessProgressSupported + } ?: false viewModelScope.launch { - _captureUiState.emit(CaptureState.CaptureFinished(outputFileResults)) + if (isProcessProgressSupported) { + _captureUiState.emit(CaptureState.CaptureProcessProgress(100)) + } + _captureUiState.emit( + CaptureState.CaptureFinished( + outputFileResults, + isProcessProgressSupported + ) + ) } } @@ -242,6 +282,18 @@ class CameraExtensionsViewModel( _captureUiState.emit(CaptureState.CaptureFailed(exception)) } } + + override fun onCaptureProcessProgressed(progress: Int) { + viewModelScope.launch { + _captureUiState.emit(CaptureState.CaptureProcessProgress(progress)) + } + } + + override fun onPostviewBitmapAvailable(bitmap: Bitmap) { + viewModelScope.launch { + _captureUiState.emit(CaptureState.CapturePostview(bitmap)) + } + } }) } diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt index 0badce1f..13aecb06 100644 --- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt +++ b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt @@ -16,6 +16,7 @@ package com.example.android.cameraxextensions.viewstate +import android.graphics.Bitmap import com.example.android.cameraxextensions.adapter.CameraExtensionItem /** @@ -25,7 +26,9 @@ import com.example.android.cameraxextensions.adapter.CameraExtensionItem data class CameraPreviewScreenViewState( val shutterButtonViewState: ShutterButtonViewState = ShutterButtonViewState(), val switchLensButtonViewState: SwitchLensButtonViewState = SwitchLensButtonViewState(), - val extensionsSelectorViewState: CameraExtensionSelectorViewState = CameraExtensionSelectorViewState() + val extensionsSelectorViewState: CameraExtensionSelectorViewState = CameraExtensionSelectorViewState(), + val processProgressViewState: ProcessProgressIndicatorViewState = ProcessProgressIndicatorViewState(), + val postviewViewState: PostviewViewState = PostviewViewState() ) { fun hideCameraControls(): CameraPreviewScreenViewState = copy( @@ -49,6 +52,17 @@ data class CameraPreviewScreenViewState( fun setAvailableExtensions(extensions: List): CameraPreviewScreenViewState = copy(extensionsSelectorViewState = extensionsSelectorViewState.copy(extensions = extensions)) + + fun showPostview(bitmap: Bitmap): CameraPreviewScreenViewState = + copy(postviewViewState = PostviewViewState(isVisible = true, bitmap = bitmap)) + + fun hidePostview(): CameraPreviewScreenViewState = copy(postviewViewState = PostviewViewState()) + + fun showProcessProgressViewState(progress: Int): CameraPreviewScreenViewState = + copy(processProgressViewState = ProcessProgressIndicatorViewState(isVisible = true, progress = progress)) + + fun hideProcessProgressViewState(): CameraPreviewScreenViewState = + copy(processProgressViewState = ProcessProgressIndicatorViewState()) } data class CameraExtensionSelectorViewState( @@ -64,4 +78,14 @@ data class ShutterButtonViewState( data class SwitchLensButtonViewState( val isVisible: Boolean = false, val isEnabled: Boolean = false -) \ No newline at end of file +) + +data class ProcessProgressIndicatorViewState( + val isVisible: Boolean = false, + val progress: Int = 0 +) + +data class PostviewViewState( + val isVisible: Boolean = false, + val bitmap: Bitmap? = null +) diff --git a/CameraXExtensions/app/src/main/res/layout/activity_main.xml b/CameraXExtensions/app/src/main/res/layout/activity_main.xml index 617f1443..d1c82264 100644 --- a/CameraXExtensions/app/src/main/res/layout/activity_main.xml +++ b/CameraXExtensions/app/src/main/res/layout/activity_main.xml @@ -73,6 +73,17 @@ app:srcCompat="@drawable/ic_flip_camera_android" app:tint="@color/button" /> + + + + + + + + + You can\'t use camera extensions unless CameraX Extensions has access to your camera. Allow CameraX Extensions access to your camera to try camera extensions. Turn On + + Hold Still \ No newline at end of file diff --git a/CameraXExtensions/build.gradle b/CameraXExtensions/build.gradle index 8b68c98f..b32135f3 100644 --- a/CameraXExtensions/build.gradle +++ b/CameraXExtensions/build.gradle @@ -20,17 +20,16 @@ buildscript { ext.kotlin_version = '1.9.22' ext.java_version = JavaVersion.VERSION_11 - ext.camerax_version = '1.4.0-alpha03' - ext.coroutines_version = '1.6.0' - ext.lifecycle_version = '2.5.1' - ext.camerax = '1.2.0-alpha04' + ext.camerax_version = '1.4.0-beta01' + ext.coroutines_version = '1.8.1' + ext.lifecycle_version = '2.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.android.tools.build:gradle:8.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/CameraXExtensions/gradle.properties b/CameraXExtensions/gradle.properties index fc8b66af..fad33040 100644 --- a/CameraXExtensions/gradle.properties +++ b/CameraXExtensions/gradle.properties @@ -36,4 +36,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties b/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties index f357f21f..3d714831 100644 --- a/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties +++ b/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties @@ -16,7 +16,7 @@ #Wed Aug 03 16:12:29 EDT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME