Skip to content

[CameraX β€’ Compose] Migrate CameraXBasic Samples to Compose #295

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

Merged
merged 12 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ dependencies {
implementation(project(":shared"))
implementation(project(":samples:accessibility"))
implementation(project(":samples:camera:camera2"))
implementation(project(":samples:camera:camerax"))
implementation(project(":samples:connectivity:audio"))
implementation(project(":samples:connectivity:bluetooth:ble"))
implementation(project(":samples:connectivity:bluetooth:companion"))
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/com/example/platform/app/ApiSurface.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ val CameraCamera2ApiSurface = ApiSurface(
null,
)

val CameraCameraXApiSurface = ApiSurface(
"camera-camerax",
"CameraX",
null,
)

val ConnectivityAudioApiSurface = ApiSurface(
"connectivity-audio",
"Connectivity Audio",
Expand Down Expand Up @@ -192,6 +198,7 @@ val UserInterfaceWindowManagerApiSurface = ApiSurface(
val API_SURFACES = listOf(
AccessiblityApiSurface,
CameraCamera2ApiSurface,
CameraCameraXApiSurface,
ConnectivityAudioApiSurface,
ConnectivityBluetoothBleApiSurface,
ConnectivityBluetoothCompanionApiSurface,
Expand Down
51 changes: 32 additions & 19 deletions app/src/main/java/com/example/platform/app/SampleDemo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.example.platform.accessibility.SpeakableText
import com.example.platform.camera.imagecapture.Camera2ImageCapture
import com.example.platform.camera.imagecapture.Camera2UltraHDRCapture
import com.example.platform.camera.preview.Camera2Preview
import com.example.platform.camerax.basic.CameraXBasic
import com.example.platform.connectivity.audio.AudioCommsSample
import com.example.platform.connectivity.bluetooth.ble.BLEScanIntentSample
import com.example.platform.connectivity.bluetooth.ble.ConnectGATTSample
Expand Down Expand Up @@ -217,6 +218,18 @@ val SAMPLE_DEMOS by lazy {
tags = listOf("Camera2"),
content = { AndroidFragment<Camera2Preview>() },
),

// CameraX Samples
ComposableSampleDemo(
id = "camerax-basic",
name = "CameraX β€’ Basic Image Capture",
description = "This sample demonstrates how to capture an image & tap-to-focus using CameraX",
documentation = "https://developer.android.com/training/camerax",
apiSurface = CameraCameraXApiSurface,
tags = listOf("CameraX"),
content = { CameraXBasic() },
),

ComposableSampleDemo(
id = "communication-audio-manager",
name = "Communication Audio Manager",
Expand Down Expand Up @@ -956,7 +969,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://source.android.com/docs/core/interaction/haptics",
apiSurface = UserInterfaceHapticsApiSurface,
tags = listOf("Haptics"),
content = { HapticsBasic() }
content = { HapticsBasic() },
),
ComposableSampleDemo(
id = "haptics-2-resist",
Expand All @@ -965,7 +978,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://source.android.com/docs/core/interaction/haptics",
apiSurface = UserInterfaceHapticsApiSurface,
tags = listOf("Haptics"),
content = { Resist() }
content = { Resist() },
),
ComposableSampleDemo(
id = "haptics-3-expand",
Expand All @@ -974,7 +987,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://source.android.com/docs/core/interaction/haptics",
apiSurface = UserInterfaceHapticsApiSurface,
tags = listOf("Haptics"),
content = { Expand() }
content = { Expand() },
),
ComposableSampleDemo(
id = "haptics-4-bounce",
Expand All @@ -983,7 +996,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://source.android.com/docs/core/interaction/haptics",
apiSurface = UserInterfaceHapticsApiSurface,
tags = listOf("Haptics"),
content = { Bounce() }
content = { Bounce() },
),
ComposableSampleDemo(
id = "haptics-5-wobble",
Expand All @@ -992,7 +1005,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://source.android.com/docs/core/interaction/haptics",
apiSurface = UserInterfaceHapticsApiSurface,
tags = listOf("Haptics"),
content = { Wobble() }
content = { Wobble() },
),
ComposableSampleDemo(
id = "live-updates",
Expand All @@ -1014,23 +1027,23 @@ val SAMPLE_DEMOS by lazy {
description = "Basic usage of Picture-in-Picture mode showcasing video playback",
documentation = "https://developer.android.com/develop/ui/views/picture-in-picture",
apiSurface = UserInterfacePictureInPictureApiSurface,
content = PiPMovieActivity::class.java
content = PiPMovieActivity::class.java,
),
ActivitySampleDemo(
id = "picture-in-picture-stopwatch",
name = "Picture in Picture (PiP) - Stopwatch",
description = "Basic usage of Picture-in-Picture mode showcasing a stopwatch",
documentation = "https://developer.android.com/develop/ui/views/picture-in-picture",
apiSurface = UserInterfacePictureInPictureApiSurface,
content = PiPSampleActivity::class.java
content = PiPSampleActivity::class.java,
),
ActivitySampleDemo(
id = "predictive-back",
name = "Predictive Back",
description = "Shows Predictive Back animations.",
documentation = "https://developer.android.com/about/versions/14/features/predictive-back",
apiSurface = UserInterfacePredictiveBackApiSurface,
content = PBHostingActivity::class.java
content = PBHostingActivity::class.java,
),
ComposableSampleDemo(
id = "quick-settings",
Expand All @@ -1051,15 +1064,15 @@ val SAMPLE_DEMOS by lazy {
description = "Receive texts and images from other apps.",
documentation = null,
apiSurface = UserInterfaceShareApiSurface,
content = ShareReceiverActivity::class.java
content = ShareReceiverActivity::class.java,
),
ComposableSampleDemo(
id = "send-data-with-sharesheet",
name = "Send data with sharesheet",
description = "Send texts and images to other apps using the Android Sharesheet.",
documentation = null,
apiSurface = UserInterfaceShareApiSurface,
content = { ShareSender() }
content = { ShareSender() },
),
ComposableSampleDemo(
id = "conversion-suggestions",
Expand All @@ -1068,7 +1081,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://developer.android.com/about/versions/13/features#text-conversion",
apiSurface = UserInterfaceTextApiSurface,
tags = listOf("Text"),
content = { AndroidFragment<ConversionSuggestions>() }
content = { AndroidFragment<ConversionSuggestions>() },
),
ComposableSampleDemo(
id = "downloadable-fonts",
Expand All @@ -1077,7 +1090,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://developer.android.com/develop/ui/views/text-and-emoji/downloadable-fonts",
apiSurface = UserInterfaceTextApiSurface,
tags = listOf("Text"),
content = { AndroidFragment<DownloadableFontsFragment>() }
content = { AndroidFragment<DownloadableFontsFragment>() },
),
ComposableSampleDemo(
id = "hyphenation",
Expand All @@ -1086,7 +1099,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://developer.android.com/reference/android/widget/TextView#attr_android:hyphenationFrequency",
apiSurface = UserInterfaceTextApiSurface,
tags = listOf("Text"),
content = { AndroidFragment<Hyphenation>() }
content = { AndroidFragment<Hyphenation>() },
),
ComposableSampleDemo(
id = "line-break",
Expand All @@ -1095,7 +1108,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://developer.android.com/about/versions/13/features#japanese-wrapping",
apiSurface = UserInterfaceTextApiSurface,
tags = listOf("Text"),
content = { AndroidFragment<LineBreak>() }
content = { AndroidFragment<LineBreak>() },
),
ComposableSampleDemo(
id = "linkify",
Expand All @@ -1104,7 +1117,7 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://developer.android.com/reference/kotlin/androidx/core/text/util/LinkifyCompat",
apiSurface = UserInterfaceTextApiSurface,
tags = listOf("Text"),
content = { AndroidFragment<Linkify>() }
content = { AndroidFragment<Linkify>() },
),
ComposableSampleDemo(
id = "text-span",
Expand All @@ -1113,31 +1126,31 @@ val SAMPLE_DEMOS by lazy {
documentation = "https://developer.android.com/kotlin/ktx#core",
apiSurface = UserInterfaceTextApiSurface,
tags = listOf("Text"),
content = { AndroidFragment<TextSpanFragment>() }
content = { AndroidFragment<TextSpanFragment>() },
),
ComposableSampleDemo(
id = "immersive-mode",
name = "Immersive mode",
description = "Immersive mode enables your app to display full-screen by hiding system bars.",
documentation = "https://developer.android.com/develop/ui/views/layout/immersive",
apiSurface = UserInterfaceWindowInsetsApiSurface,
content = { ImmersiveMode() }
content = { ImmersiveMode() },
),
ActivitySampleDemo(
id = "window-insets-animation",
name = "WindowInsetsAnimation",
description = "Shows how to react to the on-screen keyboard (IME) changing visibility, and also controlling the IME's visibility.",
documentation = "https://developer.android.com/develop/ui/views/layout/sw-keyboard",
apiSurface = UserInterfaceWindowInsetsApiSurface,
content = WindowInsetsAnimationActivity::class.java
content = WindowInsetsAnimationActivity::class.java,
),
ActivitySampleDemo(
id = "window-manager",
name = "WindowManager",
description = "Demonstrates how to use the Jetpack WindowManager library.",
documentation = "https://developer.android.com/jetpack/androidx/releases/window",
apiSurface = UserInterfaceWindowManagerApiSurface,
content = WindowDemosActivity::class.java
content = WindowDemosActivity::class.java,
),
).associateBy { it.id }
}
15 changes: 15 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ androidxTestExtTruth = "1.5.0"
androidxTestRules = "1.5.0"
androidxTestRunner = "1.5.2"
androidxUiAutomator = "2.2.0"
camerax = "1.5.0-beta01"
material3Android = "1.3.2"
media3 = "1.5.0"
constraintlayout = "2.1.4"
Expand All @@ -59,6 +60,8 @@ glance = "1.1.0"
tensorflowLite = "2.9.0"
tensorflowLiteGpuDelegatePlugin = "0.4.4"
tensorflowLiteSupport = "0.4.2"
barcodeScanningCommon = "17.0.0"
playServicesMlkitBarcodeScanning = "18.3.1"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand Down Expand Up @@ -170,6 +173,16 @@ androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", versi
androidx-media3-transformer = { module = "androidx.media3:media3-transformer", version.ref = "media3" }
androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" }

# CameraX
androidx-camerax-core = { module = "androidx.camera:camera-core", version.ref = "camerax" }
androidx-camerax-compose = { module = "androidx.camera:camera-compose", version.ref = "camerax" }
androidx-camerax-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" }
androidx-camerax-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" }
androidx-camerax-video = { module = "androidx.camera:camera-video", version.ref = "camerax" }
androidx-camerax-view = { module = "androidx.camera:camera-view", version.ref = "camerax" }
androidx-camerax-mlkit-vision = { module = "androidx.camera:camera-mlkit-vision", version.ref = "camerax" }
androidx-camerax-extensions = { module = "androidx.camera:camera-extensions", version.ref = "camerax" }

fresco = "com.facebook.fresco:fresco:3.0.0"
fresco-nativeimagetranscoder = "com.facebook.fresco:nativeimagetranscoder:2.6.0!!"
glide = "com.github.bumptech.glide:glide:4.15.1"
Expand All @@ -181,6 +194,8 @@ tensorflow-lite-gpu = { module = "org.tensorflow:tensorflow-lite-gpu", version.r
tensorflow-lite-gpu-delegate-plugin = { module = "org.tensorflow:tensorflow-lite-gpu-delegate-plugin", version.ref = "tensorflowLiteGpuDelegatePlugin" }
tensorflow-lite-select-tf-ops = { module = "org.tensorflow:tensorflow-lite-select-tf-ops", version.ref = "tensorflowLite" }
tensorflow-lite-support = { module = "org.tensorflow:tensorflow-lite-support", version.ref = "tensorflowLiteSupport" }
barcode-scanning-common = { group = "com.google.mlkit", name = "barcode-scanning-common", version.ref = "barcodeScanningCommon" }
play-services-mlkit-barcode-scanning = { group = "com.google.android.gms", name = "play-services-mlkit-barcode-scanning", version.ref = "playServicesMlkitBarcodeScanning" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down
1 change: 1 addition & 0 deletions samples/camera/camerax/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TBD
65 changes: 65 additions & 0 deletions samples/camera/camerax/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "com.example.platform.camerax"
compileSdk = 35

defaultConfig {
minSdk = 21
testOptions.targetSdk = 35
}
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.material3)
implementation(project(":shared"))

// CameraX
implementation(libs.androidx.camerax.core)
implementation(libs.androidx.camerax.camera2)
implementation(libs.androidx.camerax.compose)
implementation(libs.androidx.camerax.extensions)
implementation(libs.androidx.camerax.lifecycle)
implementation(libs.androidx.camerax.mlkit.vision)
implementation(libs.androidx.camerax.video)
implementation(libs.androidx.camerax.view)

// Image loading
implementation(libs.coil)
implementation(libs.coil.compose)

// Barcode Scanning
implementation(libs.barcode.scanning.common)
implementation(libs.play.services.mlkit.barcode.scanning)

// Permissions
implementation(libs.accompanist.permissions)
}
41 changes: 41 additions & 0 deletions samples/camera/camerax/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2023 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />

<application>

<!-- Register file provider to create shareable URIs -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" />
</provider>
</application>
</manifest>
Loading
Loading