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

PostCapture screen implementation with in memory Image storage #32

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ dependencies {

// Settings Screen
implementation(project(":feature:settings"))

// PostCapture Screen
implementation(project(":feature:postcapture"))
}

// Allow references to generated code
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/java/com/google/jetpackcamera/ui/JcaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import androidx.navigation.compose.rememberNavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.jetpackcamera.feature.postcapture.PostCaptureScreen
import com.google.jetpackcamera.feature.preview.PreviewScreen
import com.google.jetpackcamera.settings.SettingsScreen
import com.google.jetpackcamera.ui.Routes.PostCaptureRoute
import com.google.jetpackcamera.ui.Routes.PreviewRoute
import com.google.jetpackcamera.ui.Routes.SettingsRoute

Expand All @@ -53,9 +55,13 @@ private fun JetpackCameraNavHost(
NavHost(navController = navController, startDestination = PreviewRoute) {
composable(PreviewRoute) {
PreviewScreen(
onNavigateToSettings = { navController.navigate(SettingsRoute) }
onNavigateToSettings = { navController.navigate(SettingsRoute) },
onNavigateToPostCapture = { navController.navigate(PostCaptureRoute) }
)
}
composable(SettingsRoute) { SettingsScreen(navController = navController) }
composable(PostCaptureRoute) { PostCaptureScreen(
onBackPressed = { navController.navigateUp() }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we'd only be navigating to this page from within the app, we can just use .popBackStack()
they behave the same, except when using navigateUp() will return to the other app that brought you to a page. wont have to worry about that here.

Regardless, right now we can't use either without the app crashing (after rebinding use cases), i found that to be the case when navigating back to preview from the settings page. A quick fix for this is to just use navController.navigate(PreviewRoute), though the bug causing this issue should still be addressed; i think we have already touched on it.

) }
}
}
1 change: 1 addition & 0 deletions app/src/main/java/com/google/jetpackcamera/ui/Routes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ package com.google.jetpackcamera.ui
object Routes {
const val PreviewRoute = "preview"
const val SettingsRoute = "settings"
const val PostCaptureRoute = "postcapture"
}
1 change: 1 addition & 0 deletions data/storage/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
70 changes: 70 additions & 0 deletions data/storage/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (C) 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
*
* http://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 {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
}

android {
namespace = "com.google.jetpackcamera.storage"
compileSdk = 33

defaultConfig {
minSdk = 21

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}

dependencies {

implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")


// Hilt
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-compiler:2.44")
}

kapt {
correctErrorTypes = true
}
Empty file added data/storage/consumer-rules.pro
Empty file.
21 changes: 21 additions & 0 deletions data/storage/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
20 changes: 20 additions & 0 deletions data/storage/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 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
~
~ http://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">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) 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
*
* http://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.
*/

package com.google.jetpackcamera.storage

import android.graphics.Bitmap
import android.media.Image

/**
* Used to store a single [Image].
*/
interface ImageCache {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: You can replace the set/get with a var

interface ImageCache {
    var image: Bitmap?
}

class ImageCacheImpl : ImageCache {
    override var image: Bitmap?
        get() = TODO("Not yet implemented")
        set(value) {}
}

/**
* Save the image into the cache.
*/
fun setImage(image: Bitmap)

/**
* Retrieve the saved image from cache.
*/
fun getImage() : Bitmap?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 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
*
* http://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.
*/

package com.google.jetpackcamera.storage

import android.graphics.Bitmap
import android.media.Image
import android.util.Log
import javax.inject.Inject
import javax.inject.Singleton


private const val TAG = "InMemoryImageCache"

/**
* In-memory implementation of [ImageCache].
*/
class InMemoryImageCache @Inject constructor() : ImageCache {

private var image : Bitmap? = null

override fun setImage(image: Bitmap) {
// Log.d(TAG, "setImage $image ($this) ${image.format}")
this.image = image
}

override fun getImage(): Bitmap? {
Log.d(TAG, "getImage $image ($this)")
return image
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 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
*
* http://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.
*/

package com.google.jetpackcamera.storage

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
class StorageModule {

@Provides
@Singleton
fun provideImageCache() : ImageCache = InMemoryImageCache()
}
1 change: 1 addition & 0 deletions domain/camera/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
kapt "com.google.dagger:hilt-compiler:2.44"

implementation(project(":core:common"))
implementation(project(":data:storage"))
}

// Allow references to generated code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.google.jetpackcamera.domain.camera

import android.app.Application
import android.content.ContentValues
import android.graphics.BitmapFactory
import android.provider.MediaStore
import android.util.Log
import android.util.Rational
Expand All @@ -37,13 +38,14 @@ import androidx.camera.video.VideoCapture
import androidx.concurrent.futures.await
import androidx.core.content.ContextCompat
import androidx.core.util.Consumer
import androidx.lifecycle.LifecycleOwner
import com.google.jetpackcamera.storage.ImageCache
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asExecutor
import java.util.Date
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import java.nio.ByteBuffer
import java.util.Date
import javax.inject.Inject

private const val TAG = "CameraXCameraUseCase"
Expand All @@ -54,7 +56,8 @@ private val ASPECT_RATIO_16_9 = Rational(16, 9)
*/
class CameraXCameraUseCase @Inject constructor(
private val application: Application,
private val defaultDispatcher: CoroutineDispatcher
private val defaultDispatcher: CoroutineDispatcher,
private val imageCache: ImageCache
) : CameraUseCase {
private lateinit var cameraProvider: ProcessCameraProvider

Expand Down Expand Up @@ -103,8 +106,9 @@ class CameraXCameraUseCase @Inject constructor(
awaitCancellation()
}
}

@androidx.camera.core.ExperimentalGetImage
override suspend fun takePicture() {

val imageDeferred = CompletableDeferred<ImageProxy>()

imageCaptureUseCase.takePicture(
Expand All @@ -116,10 +120,18 @@ class CameraXCameraUseCase @Inject constructor(
}

override fun onError(exception: ImageCaptureException) {
super.onError(exception)
Log.d(TAG, "takePicture onError: $exception")
}
})

imageDeferred.await().let {
val buffer: ByteBuffer = it.getPlanes().get(0).getBuffer()
val bytes = ByteArray(buffer.capacity())
buffer[bytes]
val bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, null)
it.close()
imageCache.setImage(bitmapImage)
}
}

override suspend fun startVideoRecording() {
Expand Down
1 change: 1 addition & 0 deletions feature/postcapture/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Loading