Skip to content
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
7 changes: 5 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ android {
applicationId = "com.nativeapptemplate.nativeapptemplatefree"
targetSdk = 35
minSdk = 26
versionCode = 1
versionName = "1.0.0"
versionCode = 2
versionName = "2.0.0"

vectorDrawables {
useSupportLibrary = true
Expand Down Expand Up @@ -140,10 +140,13 @@ dependencies {
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.profileinstaller)
implementation(libs.androidx.tracing.ktx)
implementation(libs.capturable)
implementation(libs.compose.qr.code)
implementation(libs.hilt.android)
implementation(libs.kotlin.stdlib.jdk8)
implementation(libs.kotlinx.coroutines.guava)
implementation(libs.kotlinx.serialization.json)
implementation(libs.lottie.compose)
implementation(libs.okhttp)
implementation(libs.okhttp.logging.interceptor)
implementation(libs.retrofit)
Expand Down
24 changes: 24 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />

<application
android:name=".NativeAppTemplateApplication"
android:allowBackup="true"
Expand All @@ -18,6 +21,10 @@
tools:targetApi="tiramisu">
<profileable android:shell="true" tools:targetApi="q" />

<!--
`singleTask` for Background Tag Reading. Avoid running MainActivity onCreate again with Background Tag Reading.
https://qiita.com/takagimeow/items/48b37c55ad8d73d5da88
-->
<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -34,6 +41,23 @@
<data android:scheme="mailto" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"
android:host="api.nativeapptemplate.com"
android:path="/scan"
/>
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
17 changes: 17 additions & 0 deletions app/src/main/assets/item_tag.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"data": {
"id": "9712F2DF-DFC7-A3AA-66BC-191203654A1A",
"type": "item_tag",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A001",
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
}
}
}
49 changes: 49 additions & 0 deletions app/src/main/assets/item_tags.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"data": [
{
"id": "9712F2DF-DFC7-A3AA-66BC-191203654A1A",
"type": "item_tag",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A001",
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
}
},
{
"id": "9712F2DF-DFC7-A3AA-66BC-191203654A1",
"type": "shop",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A002",
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
}
},
{
"id": "9712F2DF-DFC7-A3AA-66BC-191203654A1C",
"type": "shop",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A003",
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
}
}
]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.nativeapptemplate.nativeapptemplatefree

import android.content.Intent
import android.graphics.Color
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
Expand All @@ -22,13 +26,16 @@ import com.nativeapptemplate.nativeapptemplatefree.MainActivityUiState.Success
import com.nativeapptemplate.nativeapptemplatefree.data.login.LoginRepository
import com.nativeapptemplate.nativeapptemplatefree.designsystem.theme.NatTheme
import com.nativeapptemplate.nativeapptemplatefree.model.DarkThemeConfig
import com.nativeapptemplate.nativeapptemplatefree.model.ItemTagInfoFromNdefMessage
import com.nativeapptemplate.nativeapptemplatefree.ui.app_root.NatApp
import com.nativeapptemplate.nativeapptemplatefree.ui.app_root.rememberNatAppState
import com.nativeapptemplate.nativeapptemplatefree.utils.NetworkMonitor
import com.nativeapptemplate.nativeapptemplatefree.utils.Utility
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.util.Date
import javax.inject.Inject

@AndroidEntryPoint
Expand All @@ -47,6 +54,16 @@ class MainActivity : ComponentActivity() {

var uiState: MainActivityUiState by mutableStateOf(Loading)

viewModel.updateShouldNavigateToScanView(false)
viewModel.updateShouldFetchItemTagForShowTagInfoScan(false)
viewModel.updateShouldCompleteItemTagForCompleteScan(false)
viewModel.initScanViewSelectedTabIndex()
viewModel.initShowTagInfoScanResult()
viewModel.initCompleteScanResult()

// viewModel.updateDidShowTapShopBelowTip(false)
// viewModel.updateDidShowReadInstructionsTip(false)

// Update the uiState
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
Expand Down Expand Up @@ -103,12 +120,81 @@ class MainActivity : ComponentActivity() {
NatApp(appState)
}
}

val intent = intent
if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
viewModel.updateShouldNavigateToScanView(false)

val ndefMessage: NdefMessage?
val rawMessages = if (SDK_INT >= 33) { // TIRAMISU
intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES,
NdefMessage::class.java
)
}else{
@Suppress("DEPRECATION")
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
}

if (!rawMessages.isNullOrEmpty()) {
ndefMessage = rawMessages[0] as NdefMessage

val itemTagInfoFromNdefMessage = Utility.extractItemTagInfoFrom(
context = this,
ndefMessage = ndefMessage
)

updateItemTagInfoFromNdefMessage(itemTagInfoFromNdefMessage)
viewModel.initScanViewSelectedTabIndex()
viewModel.updateShouldNavigateToScanView(true)
}
}
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
viewModel.updateShouldNavigateToScanView(false)

val ndefMessage: NdefMessage?
val rawMessages = if (SDK_INT >= 33) { // TIRAMISU
intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES,
NdefMessage::class.java
)
}else{
@Suppress("DEPRECATION")
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
}

if (!rawMessages.isNullOrEmpty()) {
ndefMessage = rawMessages[0] as NdefMessage

val itemTagInfoFromNdefMessage = Utility.extractItemTagInfoFrom(
context = this,
ndefMessage = ndefMessage
)

updateItemTagInfoFromNdefMessage(itemTagInfoFromNdefMessage)
viewModel.initScanViewSelectedTabIndex()
viewModel.updateShouldNavigateToScanView(true)
}
}
}

override fun onResume() {
super.onResume()
viewModel.updatePermissions()
}

private fun updateItemTagInfoFromNdefMessage(itemTagInfoFromNdefMessage: ItemTagInfoFromNdefMessage) {
if (itemTagInfoFromNdefMessage.success) {
itemTagInfoFromNdefMessage.scannedAt = Date().toInstant().toString()
}

viewModel.updateItemTagInfoFromNdefMessage(itemTagInfoFromNdefMessage)
viewModel.updateShouldCompleteItemTagForCompleteScan(itemTagInfoFromNdefMessage.success)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import androidx.lifecycle.viewModelScope
import com.nativeapptemplate.nativeapptemplatefree.MainActivityUiState.Loading
import com.nativeapptemplate.nativeapptemplatefree.MainActivityUiState.Success
import com.nativeapptemplate.nativeapptemplatefree.data.login.LoginRepository
import com.nativeapptemplate.nativeapptemplatefree.model.CompleteScanResult
import com.nativeapptemplate.nativeapptemplatefree.model.CompleteScanResultType
import com.nativeapptemplate.nativeapptemplatefree.model.ItemTagInfoFromNdefMessage
import com.nativeapptemplate.nativeapptemplatefree.model.Permissions
import com.nativeapptemplate.nativeapptemplatefree.model.ShowTagInfoScanResult
import com.nativeapptemplate.nativeapptemplatefree.model.UserData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
Expand All @@ -32,6 +36,54 @@ class MainActivityViewModel @Inject constructor(
started = SharingStarted.WhileSubscribed(5_000),
)

fun updateShouldFetchItemTagForShowTagInfoScan(shouldFetchItemTagForShowTagInfoScan: Boolean) {
viewModelScope.launch {
loginRepository.setShouldFetchItemTagForShowTagInfoScan(shouldFetchItemTagForShowTagInfoScan)
}
}

fun updateShouldCompleteItemTagForCompleteScan(shouldCompleteItemTagForCompleteScan: Boolean) {
viewModelScope.launch {
loginRepository.setShouldCompleteItemTagForCompleteScan(shouldCompleteItemTagForCompleteScan)
}
}

fun updateShouldNavigateToScanView(shouldNavigateToScanView: Boolean) {
viewModelScope.launch {
loginRepository.setShouldNavigateToScanView(shouldNavigateToScanView)
}
}

fun initScanViewSelectedTabIndex() {
viewModelScope.launch {
loginRepository.setScanViewSelectedTabIndex(0)
}
}

fun initShowTagInfoScanResult() {
viewModelScope.launch {
loginRepository.setShowTagInfoScanResult(ShowTagInfoScanResult())
}
}

fun initCompleteScanResult() {
viewModelScope.launch {
loginRepository.setCompleteScanResult(CompleteScanResult())
}
}

fun updateDidShowTapShopBelowTip(didShowTapShopBelowTip: Boolean) {
viewModelScope.launch {
loginRepository.setDidShowTapShopBelowTip(didShowTapShopBelowTip)
}
}

fun updateDidShowReadInstructionsTip(didShowReadInstructionsTip: Boolean) {
viewModelScope.launch {
loginRepository.setDidShowReadInstructionsTip(didShowReadInstructionsTip)
}
}

fun updatePermissions() {
viewModelScope.launch {
val isLoggedIn = loginRepository.isLoggedIn().first()
Expand All @@ -58,6 +110,30 @@ class MainActivityViewModel @Inject constructor(
}
}

fun updateItemTagInfoFromNdefMessage(
itemTagInfoFromNdefMessage: ItemTagInfoFromNdefMessage,
) {
viewModelScope.launch {
val completeScanResult = CompleteScanResult()
completeScanResult.itemTagInfoFromNdefMessage = itemTagInfoFromNdefMessage

if (!completeScanResult.itemTagInfoFromNdefMessage.success) {
completeScanResult.message = itemTagInfoFromNdefMessage.message
completeScanResult.completeScanResultType = CompleteScanResultType.Failed
}

try {
loginRepository.setCompleteScanResult(completeScanResult)
} catch (exception: Exception) {
val message = exception.message
completeScanResult.message = message ?: "Unknown Error"
completeScanResult.completeScanResultType = CompleteScanResultType.Failed

loginRepository.setCompleteScanResult(completeScanResult)
}
}
}

fun isLoggedIn(): StateFlow<Boolean> = loginRepository
.isLoggedIn()
.stateIn(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.nativeapptemplate.nativeapptemplatefree

object NatConstants {
const val SCAN_PATH: String = "scan"
const val SCAN_PATH_CUSTOMER: String = "scan_customer"

const val SUPPORT_MAIL: String = "[email protected]"
const val HOW_TO_USE_URL: String = "https://myturntag.com/how"
const val SUPPORT_WEBSITE_URL: String = "https://nativeapptemplate.com"
const val FAQS_URL: String = "https://nativeapptemplate.com/faqs"
const val DISCUSSIONS_URL: String = "https://github.com/nativeapptemplate/NativeAppTemplate-Free-Android/discussions"
const val PRIVACY_POLICY_URL: String = "https://nativeapptemplate.com/privacy"
const val TERMS_OF_USE_URL: String = "https://nativeapptemplate.com/terms"
Expand Down
Loading