Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
3bab721
barcode map function
MichelleWu09 May 28, 2026
7a8aaaa
update item picking screen
MichelleWu09 May 29, 2026
dfd926b
Update .DS_Store
cachen-hi May 29, 2026
f49ae85
Merge pull request #1 from MichelleWu09/Michelle
MichelleWu09 May 29, 2026
5d30fca
Merge remote-tracking branch 'upstream/main' into Cathy
cachen-hi May 29, 2026
54b6734
Update BarcodeMapPickingScreen.kt
cachen-hi Jun 2, 2026
a6eda73
Added customer information page and product page with product name, p…
cachen-hi Jun 4, 2026
ab67dfa
Added customer information page and product page with product name, p…
cachen-hi Jun 4, 2026
ccaf672
fixed the tote and connected them
cachen-hi Jun 4, 2026
a6dbd5b
deleted unused import and changed the name the Scan picking screen
cachen-hi Jun 4, 2026
e3560a7
deleted a space
cachen-hi Jun 4, 2026
392a58a
Added a Project 2 folder
cachen-hi Jun 4, 2026
5418e2d
Revert "Added a Project 2 folder"
cachen-hi Jun 4, 2026
35c4cb2
Added a folder calledProject 2
cachen-hi Jun 4, 2026
55a2d0d
Deleted Project 2
cachen-hi Jun 4, 2026
9e18fcf
Added a Project 2 Folder
cachen-hi Jun 4, 2026
5189950
Merge branch 'yesha' of https://github.com/MichelleWu09/AISuite_Andro…
yeshak12 Jun 5, 2026
2fe616f
Merge branch 'main' into yesha
yeshak12 Jun 5, 2026
549e9f6
updated project 2
yeshak12 Jun 8, 2026
ae2c78a
Changed things
cachen-hi Jun 8, 2026
1d7b5f9
Revert "Changed things"
cachen-hi Jun 8, 2026
0b7285e
Reapply "Changed things"
cachen-hi Jun 8, 2026
b1ba946
Merge branch 'main' of https://github.com/MichelleWu09/AISuite_Androi…
cachen-hi Jun 8, 2026
5016385
Merge branch 'main' of https://github.com/MichelleWu09/AISuite_Androi…
cachen-hi Jun 8, 2026
5f1b9c2
Changed the text for saving
cachen-hi Jun 8, 2026
701beb3
Merge pull request #2 from MichelleWu09/Cathy
MichelleWu09 Jun 8, 2026
2f60cf9
Changed the text for saving
cachen-hi Jun 8, 2026
accbf2a
Merge branch 'main' of https://github.com/MichelleWu09/AISuite_Androi…
cachen-hi Jun 8, 2026
7034c44
Delete BarcodeScanPickingScreen.kt
cachen-hi Jun 8, 2026
4ef6e25
Only show the last five digits of the barcode
cachen-hi Jun 8, 2026
b57c86c
label barcodes + larger texts
MichelleWu09 Jun 8, 2026
2dfdd95
Merge branch 'main' into Michelle
MichelleWu09 Jun 8, 2026
25f4414
Merge pull request #3 from MichelleWu09/Michelle
MichelleWu09 Jun 8, 2026
7fc87dc
Merge remote-tracking branch 'upstream/main' into Cathy
cachen-hi Jun 8, 2026
71eee93
update to last five digits
MichelleWu09 Jun 8, 2026
9e0d89d
Merge pull request #4 from MichelleWu09/Michelle
MichelleWu09 Jun 8, 2026
e25b509
Merge remote-tracking branch 'upstream/main'
cachen-hi Jun 8, 2026
4cda071
Merge branch 'main' of https://github.com/MichelleWu09/AISuite_Androi…
cachen-hi Jun 8, 2026
c6586ce
Merge branch 'main' into Cathy
cachen-hi Jun 8, 2026
e6d10ee
added date and expiration checker
yeshak12 Jun 8, 2026
f98567f
Added space below "Proceed to Scanning"
cachen-hi Jun 8, 2026
d9a7c5c
Map customer to tote label
MichelleWu09 Jun 8, 2026
0be1c2c
Merge pull request #5 from MichelleWu09/Michelle
MichelleWu09 Jun 8, 2026
8b4cf57
sort by rows
MichelleWu09 Jun 8, 2026
65f44cf
Merge pull request #6 from MichelleWu09/Michelle
MichelleWu09 Jun 8, 2026
bce1844
Show the quantity instead of the barcode number on the item picking s…
cachen-hi Jun 8, 2026
5d1f2d2
Merge branch 'main' into Cathy
cachen-hi Jun 8, 2026
dea6843
Map customer info to totes
MichelleWu09 Jun 8, 2026
57a33c4
Added barriers under the title for each page
cachen-hi Jun 8, 2026
f2308b4
Merge branch 'main' into Cathy
cachen-hi Jun 8, 2026
7f97f47
Merge pull request #7 from MichelleWu09/Cathy
MichelleWu09 Jun 8, 2026
dfad0ac
add tabs and drop downs on product picking list page
MichelleWu09 Jun 8, 2026
798d306
Merge pull request #8 from MichelleWu09/Michelle
MichelleWu09 Jun 8, 2026
ed84dda
Delete total prices, larger barcode boxes
MichelleWu09 Jun 8, 2026
3cab078
Changed the title and changed item to product
cachen-hi Jun 8, 2026
fb4f152
Merge branch 'main' into Michelle
MichelleWu09 Jun 9, 2026
65a482f
Changed the Title and make sure it always show the correct one
cachen-hi Jun 9, 2026
1a0d8f1
Merge branch 'main' into Michelle
MichelleWu09 Jun 9, 2026
b4d1873
organize barcode map and more updates
MichelleWu09 Jun 9, 2026
571af97
Merge pull request #9 from MichelleWu09/Michelle
MichelleWu09 Jun 9, 2026
c66c5d6
Changed the Customer Info
cachen-hi Jun 9, 2026
ca3f105
Merge branch 'Cathy'
cachen-hi Jun 9, 2026
e938267
Update CustomerInfo.kt
cachen-hi Jun 9, 2026
c24e364
Merge branch 'Cathy'
cachen-hi Jun 9, 2026
254d28e
stopped randomized product picking list
MichelleWu09 Jun 9, 2026
62fde4f
Merge pull request #10 from MichelleWu09/Michelle
MichelleWu09 Jun 9, 2026
ea5fac4
still need to display if tote is correct
MichelleWu09 Jun 9, 2026
816af7f
Merge pull request #11 from MichelleWu09/Michelle
MichelleWu09 Jun 9, 2026
42ad9bf
Merge branch 'main' into Cathy
cachen-hi Jun 9, 2026
6d3d08f
project 2 updates
yeshak12 Jun 9, 2026
0104db4
update project 2
yeshak12 Jun 9, 2026
63ccbe3
Merge branch 'main' into yesha
yeshak12 Jun 10, 2026
075c706
Merge pull request #12 from MichelleWu09/yesha
yeshak12 Jun 10, 2026
571d53e
Did stuff with the tote
cachen-hi Jun 10, 2026
809fa5f
Merge branch 'Cathy'
cachen-hi Jun 10, 2026
353caea
change highlight color
MichelleWu09 Jun 10, 2026
da3efc7
Merge pull request #13 from MichelleWu09/Michelle
MichelleWu09 Jun 10, 2026
a5d9c9e
Change the picking feedback to incorrect Product
cachen-hi Jun 10, 2026
1b4512e
Merge branch 'Cathy'
cachen-hi Jun 10, 2026
d482e33
Able to scan multiple totes on the Product scan page
cachen-hi Jun 10, 2026
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
Binary file added .DS_Store
Binary file not shown.
Binary file added AISuite_Demos/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@ import com.zebra.aidatacapturedemo.ui.view.Screen

/**
* AIDataCaptureDemoUiState.kt is a data class that holds the UI state for the AI Data Capture Demo
* application. It includes various settings and results related to barcode scanning, OCR,
* Retail shelf recognition, and Product recognition. The state is updated based on user
* interactions and model outputs, allowing the UI to reactively display the current status of the
* application. This class is used to manage the state of the application and facilitate
* communication between the UI and the underlying models.
*/

val PROFILING = "Profiling"

enum class UsecaseState(val value: String) {
Main("None"),
Barcode("Barcode Recognizer"),
BarcodeMap("Barcode Map"),
OCR("Text/OCR Recognizer"),
Retail("Product & Shelf Recognizer"),
OCRBarcodeFind("OCR & Barcode Find"),
Expand Down Expand Up @@ -191,6 +187,17 @@ data class AIDataCaptureDemoUiState(
var productResults: MutableList<ProductData> = mutableListOf(),
val ocrResults: List<ResultData> = listOf(),
var barcodeResults: List<ResultData> = listOf(),
var barcodeLabels: Map<String, String> = emptyMap(),
var pickingBarcodeResults: List<ResultData> = listOf(),
var pickingBarcodeLabels: Map<String, String> = emptyMap(),
var selectedToteId: String? = null,
var allCustomers: List<CustomerInfo> = listOf(),
var selectedCustomer: CustomerInfo? = null,
var pickingFeedback: String? = null,
var lastScannedProduct: ProductInfo? = null,
var targetTotes: List<Pair<String, Int>> = listOf(), // Tote ID to Quantity
var pickedProductBarcodes: Set<String> = emptySet(),
var validatedTotes: Set<String> = emptySet(), // Tote Labels confirmed by scan

// Choices
var isBarcodeModelEnabled: Boolean = true,
Expand All @@ -201,6 +208,8 @@ data class AIDataCaptureDemoUiState(
var ocrBarcodeCaptureSessionCount : Int = 0,
var ocrBarcodeCaptureSessionIndex : Int = 0,

var extractedExpirationDate: String? = null,

var selectedFilterType: FilterType = FilterType.NONE,
var ocrFilterData: OcrFilterData = FileUtils.loadOcrFilterData(),
var barcodeFilterData: BarcodeFilterData = FileUtils.loadBarcodeFilterData()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2024-2025 Zebra Technologies Corporation and/or its affiliates. All rights reserved.

package com.zebra.aidatacapturedemo.data

import kotlin.random.Random

data class ProductInfo(
val name: String,
val price: Double,
val barcode: String,
val quantity: Int = Random.nextInt(1, 10)
)

data class CustomerInfo(
val id: String,
val products: List<ProductInfo>
)

object CustomerDataGenerator {
private val availableProducts = listOf(
ProductInfo("Purell Hand Sanitizer", 5.99, "073852401097"),
ProductInfo("Maltese", 2.49, "6936749026602"),
ProductInfo("Clip", 3.95, "6936590040130"),
ProductInfo("Premium Skincare Facial Tissue", 7.49, "627987553635"),
ProductInfo("Blue Pen", 1.49, "4901681143122"),
ProductInfo("Red Pen", 1.49, "4901681143139"),
ProductInfo("Green Highlighter", 1.79, "045888781405"),
ProductInfo("Yellow Highlighter", 1.79, "045888783508"),
ProductInfo("Staedtler Mars Plastic Erazer", 2.25, "031901907983"),
ProductInfo("Uni-Ball Black Pen", 1.99, "4902778497814"),
ProductInfo("Ain Stein 0.5 HB", 1.55, "4902506269249"),
ProductInfo("Black board", 1.79, "024680135791"),
ProductInfo("Pencil", 1.25, "4007817182260"),
ProductInfo("Equate Instant Hand Sanitizer Gel", 1.25, "628915089622"),
ProductInfo("Math Sudoku", 1.25, "4952583053415"),
)

fun generateCustomers(toteIds: List<String> = listOf("A", "B", "C", "D", "E", "F")): List<CustomerInfo> {
return toteIds.map { id ->
val numProducts = Random.nextInt(1, 4)
val selectedProducts = availableProducts.shuffled().take(numProducts).map {
it.copy(quantity = Random.nextInt(1, 6))
}
CustomerInfo(id, selectedProducts)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.zebra.aidatacapturedemo.model

import com.zebra.aidatacapturedemo.data.ResultData
import java.util.regex.Pattern

/**
* Utility class to extract expiration dates from OCR text results based on product label analysis rules.
*/
object ExpirationDateParser {

private val KEYWORDS = listOf(
"EXP", "EXPIRY", "EXPIRES", "EXPIRATION DATE",
"BEST BEFORE", "BEST BY", "BB",
"MA", "MFG"
)

private val EXP_KEYWORDS = listOf("EXP", "EXPIRY", "EXPIRES", "EXPIRATION DATE")

// Regex for various date formats as requested:
// 1. MM/YY or MM/YYYY (e.g. 12/26, 12/2026)
// 2. MM-YY or MM-YYYY (e.g. 12-26, 12-2026)
// 3. MON YYYY (e.g. JAN 2026)
// 4. YYYY-MM-DD (e.g. 2026-12-31)
private const val DATE_PATTERN_STR =
"""(\b\d{1,2}[/-]\d{2,4}\b|\b(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\s+\d{4}\b|\b\d{4}-\d{2}-\d{2}\b)"""

private val KEYWORD_PATTERN_STR = KEYWORDS.joinToString("|") { Pattern.quote(it) }

/**
* Extracts the expiration date from the given OCR text.
*
* @param ocrText The full text obtained from OCR.
* @return The extracted date string, or "Not found" if no matching date is found.
*/
fun extractExpirationDate(ocrText: String): String {
if (ocrText.isBlank()) return "Not found"

// Pattern to find keyword followed by optional separator (: or space) and then the date
val regex = Regex("(?i)($KEYWORD_PATTERN_STR)[:\\s]*$DATE_PATTERN_STR", RegexOption.IGNORE_CASE)

val matches = regex.findAll(ocrText)
val results = mutableListOf<Pair<String, String>>()

for (match in matches) {
val keyword = match.groups[1]?.value?.uppercase() ?: ""
val date = match.groups[2]?.value ?: ""
if (date.isNotEmpty()) {
results.add(keyword to date)
}
}

if (results.isEmpty()) return "Not found"

// "If multiple dates are found, return the one labeled as expiration specifically"
val expResult = results.find { (kw, _) ->
EXP_KEYWORDS.any { kw.contains(it) }
}

return expResult?.second ?: results.first().second
}

/**
* Extracts expiration date from a list of OCR results, handling cases where the keyword
* and date might be in separate ResultData items.
*
* @param results List of ResultData objects from OCR analysis.
* @return The extracted date string, or "Not found".
*/
fun extractFromResults(results: List<ResultData>): String {
if (results.isEmpty()) return "Not found"

val foundDates = mutableListOf<Pair<String, String>>()
val keywordRegex = Regex("(?i)($KEYWORD_PATTERN_STR)", RegexOption.IGNORE_CASE)
val dateRegex = Regex(DATE_PATTERN_STR, RegexOption.IGNORE_CASE)
val combinedRegex = Regex("(?i)($KEYWORD_PATTERN_STR)[:\\s]*$DATE_PATTERN_STR", RegexOption.IGNORE_CASE)

for (i in results.indices) {
val text = results[i].text.trim()

// Case 1: Keyword and Date in the same result item
val match = combinedRegex.find(text)
if (match != null) {
foundDates.add(match.groups[1]!!.value.uppercase() to match.groups[2]!!.value)
continue
}

// Case 2: Keyword in this result, Date in the next result (sequential)
val keywordMatch = keywordRegex.find(text)
if (keywordMatch != null && i + 1 < results.size) {
val nextText = results[i+1].text.trim()
val dateMatch = dateRegex.find(nextText)
if (dateMatch != null) {
foundDates.add(keywordMatch.value.uppercase() to dateMatch.value)
}
}
}

if (foundDates.isEmpty()) {
// Last resort: search in the joined string
return extractExpirationDate(results.joinToString(" ") { it.text })
}

val expResult = foundDates.find { (kw, _) ->
EXP_KEYWORDS.any { kw.contains(it) }
}

return expResult?.second ?: foundDates.first().second
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.zebra.aidatacapturedemo.data.OcrBarcodeFindSettings
import com.zebra.aidatacapturedemo.data.OcrFilterData
import com.zebra.aidatacapturedemo.data.ProductData
import com.zebra.aidatacapturedemo.data.ProductRecognitionSettings
import com.zebra.aidatacapturedemo.data.ResultData
import com.zebra.aidatacapturedemo.data.RetailShelfSettings
import com.zebra.aidatacapturedemo.data.TextOcrSettings
import com.zebra.aidatacapturedemo.data.UsecaseState
Expand Down Expand Up @@ -243,6 +244,20 @@ class FileUtils(cacheDir: String, context : Context) {
}
}

fun saveBarcodeResultsToFile(barcodeResults: List<ResultData>) {
try {
val timestamp = getTimeStamp()
val fileName = "barcode_layout_$timestamp.json"
val file = File(mContext.getExternalFilesDir(null), fileName)
FileWriter(file).use { writer ->
gson.toJson(barcodeResults, writer)
}
Log.d(TAG, "Barcode results saved to ${file.absolutePath}")
} catch (e: Exception) {
e.printStackTrace()
}
}

private fun getTimeStamp(): String {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmssSSS"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,29 +337,32 @@ fun AIDataCaptureDemoAppBarTitle(
viewModel: AIDataCaptureDemoViewModel,
uiState: AIDataCaptureDemoUiState
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start),
verticalAlignment = Alignment.CenterVertically,
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(color = Variables.mainDefault)
.padding(start = 8.dp, top = 8.dp, end = 8.dp, bottom = 8.dp)
) {
Text(
text = uiState.appBarTitle,
softWrap = true,
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
style = TextStyle(
fontSize = 18.sp,
lineHeight = 28.sp,
fontFamily = FontFamily(Font(R.font.ibm_plex_sans_medium)),
fontWeight = FontWeight(500),
color = mainInverse,
.padding(start = 8.dp, top = 8.dp, end = 8.dp, bottom = 8.dp)
) {
Text(
text = uiState.appBarTitle,
softWrap = true,
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
style = TextStyle(
fontSize = 18.sp,
lineHeight = 28.sp,
fontFamily = FontFamily(Font(R.font.ibm_plex_sans_medium)),
fontWeight = FontWeight(500),
color = mainInverse,
)
)
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ fun AIDataCaptureTechnologyList(
viewModel.initModel()
navController.navigate(route = Screen.DemoStart.route)
})
AIDataCaptureListItem(
R.drawable.barcode_icon,
stringResource(id = R.string.barcode_map_demo),
stringResource(id = R.string.barcode_map_desc),
Variables.mainIcon2,
Variables.secondaryIcon2,
onItemClick = { selectedUsecase ->
viewModel.updateAppBarTitle(getString(context, R.string.barcode_map_demo))
viewModel.updateSelectedUsecase(selectedUsecase)
viewModel.initModel()
navController.navigate(route = Screen.DemoStart.route)
})
AIDataCaptureListItem(
R.drawable.retail_shelf_icon,
stringResource(id = R.string.retail_shelf_demo),
Expand Down
Loading