Skip to content

Commit

Permalink
Merge pull request #19 from Passiolife/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
marinPassio authored Aug 30, 2024
2 parents d54400b + 0291272 commit 206cd03
Show file tree
Hide file tree
Showing 25 changed files with 857 additions and 286 deletions.
Binary file modified passio-sdk/passiolib-release.aar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import ai.passio.nutrition.uimodule.ui.util.getStartOfWeek
import ai.passio.passiosdk.passiofood.FoodCandidates
import ai.passio.passiosdk.passiofood.FoodDetectionConfiguration
import ai.passio.passiosdk.passiofood.FoodRecognitionListener
import ai.passio.passiosdk.passiofood.NutritionFactsRecognitionListener
import ai.passio.passiosdk.passiofood.PassioFoodDataInfo
import ai.passio.passiosdk.passiofood.PassioSDK
import ai.passio.passiosdk.passiofood.data.model.PassioFoodItem
import ai.passio.passiosdk.passiofood.nutritionfacts.PassioNutritionFacts
import android.content.Context
import android.graphics.Bitmap
import kotlinx.coroutines.channels.awaitClose
Expand Down Expand Up @@ -50,7 +52,7 @@ class Repository private constructor() {

suspend fun fetchPassioFoodItem(
searchResult: PassioFoodDataInfo,
weighGrams: Double ?= null
weighGrams: Double? = null
): PassioFoodItem? = suspendCoroutine { cont ->
PassioSDK.instance.fetchFoodItemForDataInfo(searchResult, weighGrams) { foodItem ->
cont.resumeWith(Result.success(foodItem))
Expand All @@ -67,12 +69,27 @@ class Repository private constructor() {

fun stopFoodDetection() {
PassioSDK.instance.stopFoodDetection()
PassioSDK.instance.stopNutritionFactsDetection()
}

fun nutritionFactsResultFlow(): Flow<Pair<PassioNutritionFacts?, String>> = callbackFlow {
stopFoodDetection()
val callback = object : NutritionFactsRecognitionListener {
override fun onRecognitionResult(nutritionFacts: PassioNutritionFacts?, text: String) {
trySendBlocking(nutritionFacts to text)
}
}
PassioSDK.instance.startNutritionFactsDetection(callback)

awaitClose {
PassioSDK.instance.stopNutritionFactsDetection()
}
}

fun recognitionResultFlow(
config: FoodDetectionConfiguration
): Flow<FoodCandidates?> = callbackFlow {
PassioSDK.instance.stopFoodDetection()
stopFoodDetection()
val callback = object : FoodRecognitionListener {
override fun onRecognitionResults(candidates: FoodCandidates?, image: Bitmap?) {
trySendBlocking(candidates)
Expand Down Expand Up @@ -162,10 +179,10 @@ class Repository private constructor() {
return connector.removeWeightRecord(weightRecord)
}

suspend fun fetchLatestWeightRecord(): WeightRecord?
{
suspend fun fetchLatestWeightRecord(): WeightRecord? {
return connector.fetchLatestWeightRecord()
}

suspend fun fetchWeightRecords(currentDate: Date): List<WeightRecord> {
val forDate = DateTime(currentDate.time)
val startDate: DateTime = forDate.withTimeAtStartOfDay()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ object CameraUseCase {
return repository.recognitionResultFlow(config).map { mapRecognitionResult(it) }
}

fun nutritionFactsFlow(): Flow<RecognitionResult> {
return repository.nutritionFactsResultFlow()
.map {
if (it.first == null) {
RecognitionResult.NoRecognition
} else {
RecognitionResult.NutritionFactRecognition(it)
}
}
}

suspend fun fetchFoodItemForPassioID(passioID: PassioID): PassioFoodItem? {
return repository.fetchFoodItemForPassioID(passioID)
}
Expand Down Expand Up @@ -51,15 +62,19 @@ object CameraUseCase {
if (barcodeCandidate != null) {
val foodItem = repository.fetchFoodItemForProduct(barcodeCandidate.barcode)
?: return RecognitionResult.NoProductRecognition
return RecognitionResult.ProductRecognition(foodItem)
return RecognitionResult.FoodRecordRecognition(FoodRecord(foodItem).apply {
this.barcode = barcodeCandidate.barcode
})
}

val packagedCandidate =
candidates.packagedFoodCandidates?.firstOrNull() //maxByOrNull { it.confidence }
if (packagedCandidate != null) {
val foodItem = repository.fetchFoodItemForProduct(packagedCandidate.packagedFoodCode)
?: return RecognitionResult.NoProductRecognition
return RecognitionResult.ProductRecognition(foodItem)
return RecognitionResult.FoodRecordRecognition(FoodRecord(foodItem).apply {
this.packagedFoodCode = packagedCandidate.packagedFoodCode
})
}

return RecognitionResult.NoRecognition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ai.passio.nutrition.uimodule.domain.camera

import ai.passio.nutrition.uimodule.ui.model.FoodRecord
import ai.passio.passiosdk.passiofood.DetectedCandidate
import ai.passio.passiosdk.passiofood.data.model.PassioFoodItem
import ai.passio.passiosdk.passiofood.nutritionfacts.PassioNutritionFacts

sealed class RecognitionResult {
data class VisualRecognition(val visualCandidate: DetectedCandidate) : RecognitionResult()
data class ProductRecognition(val foodItem: PassioFoodItem) : RecognitionResult()
data class FoodRecordRecognition(val foodItem: FoodRecord) : RecognitionResult()
data class NutritionFactRecognition(val nutritionFactsPair: Pair<PassioNutritionFacts?, String>) : RecognitionResult()
object NoProductRecognition : RecognitionResult()
object NoRecognition : RecognitionResult()
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import ai.passio.nutrition.uimodule.ui.base.BaseToolbar
import ai.passio.nutrition.uimodule.ui.model.FoodRecord
import ai.passio.nutrition.uimodule.ui.util.ProgressDialog
import ai.passio.passiosdk.core.camera.PassioCameraViewProvider
import ai.passio.passiosdk.passiofood.DetectedCandidate
import ai.passio.passiosdk.passiofood.data.model.PassioFoodItem
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
Expand All @@ -31,6 +32,9 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
import com.warkiz.tickseekbar.OnSeekChangeListener
import com.warkiz.tickseekbar.SeekParams
import com.warkiz.tickseekbar.TickSeekBar
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -58,6 +62,10 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),
viewModel.scanModeEvent.observe(viewLifecycleOwner, ::scanModeUpdated)
viewModel.foodItemResult.observe(viewLifecycleOwner, ::editFoodItem)
viewModel.logFoodEvent.observe(viewLifecycleOwner, ::foodItemLogged)
viewModel.cameraZoomLevelRangeEvent.observe(viewLifecycleOwner, ::setupCameraZoomMode)
viewModel.cameraFlashToggleEvent.observe(viewLifecycleOwner) { isON ->
binding.cameraFlash.setImageResource(if (isON) R.drawable.ic_camera_flash_on else R.drawable.ic_camera_flash_off)
}
viewModel.showLoading.observe(viewLifecycleOwner) { isLoading ->

if (isLoading) {
Expand All @@ -84,8 +92,42 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),
// Permission is already granted
cameraPermissionGranted()
}

binding.cameraFlash.setOnClickListener {
viewModel.toggleCameraFlash()
}

binding.cameraZoomLevel.onSeekChangeListener = object : OnSeekChangeListener {
override fun onSeeking(seekParams: SeekParams?) {
if (seekParams != null && seekParams.fromUser) {
viewModel.setCameraZoomLevel(seekParams.progressFloat)
}
}

override fun onStartTrackingTouch(seekBar: TickSeekBar?) {
}

override fun onStopTrackingTouch(seekBar: TickSeekBar?) {
}

}

}

private fun setupCameraZoomMode(zoomLevel: Triple<Float, Float?, Float?>) {
with(binding) {
if (zoomLevel.second == null || zoomLevel.third == null) {
cameraZoomLevel.visibility = View.GONE
} else {
cameraZoomLevel.visibility = View.VISIBLE
cameraZoomLevel.min = zoomLevel.second!!
cameraZoomLevel.max = zoomLevel.third!!
cameraZoomLevel.setProgress(zoomLevel.first)
}
}
}


private fun initOnClickCallback() {
with(binding)
{
Expand Down Expand Up @@ -131,8 +173,7 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),

when (resultWrapper) {
is ResultWrapper.Success -> {
sharedViewModel.editFoodRecord(FoodRecord(resultWrapper.value))
viewModel.navigateToEdit()
editFoodRecord(FoodRecord(resultWrapper.value))
}

is ResultWrapper.Error -> {
Expand All @@ -146,53 +187,59 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),
}
}

private fun editFoodRecord(foodRecord: FoodRecord) {
sharedViewModel.editFoodRecord(foodRecord)
viewModel.navigateToEdit()
}

private val recognitionResultListener = object :
RecognitionResultView.RecognitionResultListener {
override fun onLogVisual(detectedCandidate: DetectedCandidate) {
viewModel.stopDetection()
with(binding) {
viewModel.logFood(detectedCandidate.passioID)
/*recognitionResult.visibility = View.GONE
scanningMessage.visibility = View.GONE
viewAddedToDiary.visibility = View.VISIBLE
recognitionResult.reset()*/
}
}

override fun onEditVisual(detectedCandidate: DetectedCandidate) {
override fun onLog(result: RecognitionResult) {
viewModel.stopDetection()
with(binding) {
// recognitionResult.visibility = View.GONE
// scanningMessage.visibility = View.GONE
// viewAddedToDiary.visibility = View.VISIBLE
// recognitionResult.reset()
when (result) {
is RecognitionResult.VisualRecognition -> {
viewModel.logFood(result.visualCandidate.passioID)
}

viewModel.fetchFoodItemToEdit(detectedCandidate.passioID)
}
}
is RecognitionResult.FoodRecordRecognition -> {
viewModel.logFoodRecord(result.foodItem)
}

override fun onEditProduct(result: RecognitionResult.ProductRecognition) {
viewModel.stopDetection()
with(binding) {
// recognitionResult.visibility = View.GONE
// scanningMessage.visibility = View.GONE
// viewAddedToDiary.visibility = View.VISIBLE
// recognitionResult.reset()
editFoodItem(ResultWrapper.Success(result.foodItem))
is RecognitionResult.NutritionFactRecognition -> {

}

else -> {

}
}
}

override fun onLogProduct(result: RecognitionResult.ProductRecognition) {
override fun onEdit(result: RecognitionResult) {
viewModel.stopDetection()
with(binding) {
viewModel.logFood(result.foodItem)
/*recognitionResult.visibility = View.GONE
scanningMessage.visibility = View.GONE
viewAddedToDiary.visibility = View.VISIBLE
recognitionResult.reset()*/
when (result) {
is RecognitionResult.VisualRecognition -> {
viewModel.fetchFoodItemToEdit(result.visualCandidate.passioID)
}

is RecognitionResult.FoodRecordRecognition -> {
editFoodRecord(result.foodItem)
}

is RecognitionResult.NutritionFactRecognition -> {

}

else -> {

}
}
}

override fun onSearchTapped() {
viewModel.navigateToSearch()
}
}

private val bottomSheetCallback = object : BottomSheetCallback() {
Expand Down Expand Up @@ -223,6 +270,7 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),

}

@SuppressLint("SetTextI18n")
private fun scanModeUpdated(scanMode: ScanMode) {
when (scanMode) {
ScanMode.VISUAL -> {
Expand Down Expand Up @@ -355,12 +403,12 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),
it.recognitionResult.reset()
}

is RecognitionResult.ProductRecognition -> {
is RecognitionResult.FoodRecordRecognition -> {
it.viewAddedToDiary.visibility = View.GONE
it.recognitionResult.visibility = View.VISIBLE
it.scanningMessage.visibility = View.GONE

it.recognitionResult.showProductResult(result)
it.recognitionResult.showFoodRecordRecognition(result)
}

is RecognitionResult.VisualRecognition -> {
Expand All @@ -370,6 +418,14 @@ class CameraRecognitionFragment : BaseFragment<CameraRecognitionViewModel>(),

it.recognitionResult.showVisualResult(result)
}

is RecognitionResult.NutritionFactRecognition -> {
it.viewAddedToDiary.visibility = View.GONE
it.recognitionResult.visibility = View.VISIBLE
it.scanningMessage.visibility = View.GONE

it.recognitionResult.showNutritionFactsResult(result)
}
}
}
}
Expand Down
Loading

0 comments on commit 206cd03

Please sign in to comment.