Feat(All): Tis

some changes
This commit is contained in:
Amir Mousavi
2026-05-06 23:33:56 +03:30
parent c9e656f705
commit d73df5524e
128 changed files with 1635 additions and 156 deletions
+12
View File
@@ -10,6 +10,18 @@
<option name="selectionMode" value="DROPDOWN" />
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="practice.PSP.tis">
<option name="selectionMode" value="DROPDOWN" />
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="tis_app">
<option name="selectionMode" value="DROPDOWN" />
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="stage_app">
<option name="selectionMode" value="DROPDOWN" />
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>
+3 -2
View File
@@ -12,9 +12,10 @@
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/core" />
<option value="$PROJECT_DIR$/design_system" />
<option value="$PROJECT_DIR$/ps3" />
<option value="$PROJECT_DIR$/p3" />
<option value="$PROJECT_DIR$/ps4" />
<option value="$PROJECT_DIR$/tis_sep" />
<option value="$PROJECT_DIR$/stage_app" />
<option value="$PROJECT_DIR$/tis_app" />
</set>
</option>
</GradleProjectSettings>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PlanningModeManager">
<option name="approvalStates">
<map>
<entry key="20260504-162003-d0f92a5d-1a15-429a-82cc-cad6ed3c0e3e" value="true" />
</map>
</option>
</component>
</project>
@@ -0,0 +1,9 @@
package com.example.core.data
import com.example.core.domain.AppConfig
class AppConfigImpl : AppConfig{
override fun androidId(id: String): String {
return "window.WebV.onUUID($id);"
}
}
@@ -10,7 +10,7 @@ class PaymentResultImpl @Inject constructor(
) : PaymentResult {
override fun post(paymentResultEntity: PaymentResultEntity?): String {
return "window.onPaymentResult(${gson.toJson(paymentResultEntity)});"
return "window.WebV.onPaymentResult(${gson.toJson(paymentResultEntity)});"
}
}
@@ -1,6 +1,8 @@
package com.example.core.di
import com.example.core.data.AppConfigImpl
import com.example.core.data.PaymentResultImpl
import com.example.core.domain.AppConfig
import com.example.core.domain.PaymentResult
import com.google.gson.Gson
import dagger.Module
@@ -23,4 +25,9 @@ class CoreModule {
): PaymentResult = PaymentResultImpl(
gson = gson
)
@Provides
fun provideAppConfig(
gson: Gson
): AppConfig = AppConfigImpl()
}
@@ -0,0 +1,7 @@
package com.example.core.domain
import com.example.core.model.PaymentResultEntity
interface AppConfig {
fun androidId(id : String) : String
}
@@ -0,0 +1,11 @@
package com.example.core.domain
import androidx.annotation.DrawableRes
import com.example.core.model.PrintEntity
interface Printer {
fun print(
printEntities: List<PrintEntity>,
@DrawableRes icon :Int)
}
@@ -0,0 +1,23 @@
package com.example.core.model
import androidx.annotation.Keep
@Keep
data class PrintEntity(
val title: String? = null,
val items: List<ItemEntity>? = null,
val paragraph: String? = null,
val type: Type
) {
data class ItemEntity(
val label: String,
val value: String,
)
enum class Type() {
CARD,
PARAGRAPH
}
}
+28
View File
@@ -0,0 +1,28 @@
<!--
~ Copyright (C) 2026 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="800dp"
android:height="200dp"
android:viewportWidth="800"
android:viewportHeight="200">
<path
android:pathData="M1.2,101.4L6.2,101.4L27.2,101.4L53.2,101.4L69.2,101.4L87.2,101.4L103.2,101.4L117.2,101.4L126.2,101.4L134.2,101.4L144.2,101.4L150.2,101.4L162.2,101.4L179.2,101.4L197.2,101.4L217.2,101.4L231.2,101.4L252.2,101.4L265.2,101.4L282.2,101.4L293.2,101.4L313.2,101.4L336.2,101.4L357.2,101.4L377.2,101.4L400.2,101.4L433.2,101.4L456.2,101.4L489.2,101.4L515.2,101.4L515.2,101.4L521.2,101.4L545.2,101.4L581.2,101.4L605.2,101.4L633.2,101.4L649.2,101.4L663.2,101.4L672.2,101.4L684.2,101.4L691.2,101.4L701.2,101.4L713.2,101.4L722.2,101.4L737.2,101.4L747.2,101.4L752.2,101.4L761.2,101.4L769.2,101.4L781.2,101.4L787.2,101.4L794.2,101.4L797.2,101.4L799.2,101.4L801.2,101.4"
android:strokeLineJoin="round"
android:strokeWidth="8"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>
@@ -0,0 +1,26 @@
<!--
~ Copyright (C) 2026 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="2dp"
android:viewportWidth="24.8"
android:viewportHeight="8">
<path
android:pathData="M0,0h24.8v248h-24.8z"
android:strokeWidth="0"
android:fillColor="#000000"
android:strokeColor="#000000"/>
</vector>
+3
View File
@@ -37,6 +37,7 @@ dependencies {
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.retrofit.converter.gson)
testImplementation(libs.junit)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
@@ -44,4 +45,6 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)
implementation(project(":core"))
}
@@ -1,8 +1,10 @@
package com.example.design_system
import android.annotation.SuppressLint
import android.app.Activity
import android.view.ViewGroup
import android.webkit.JavascriptInterface
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
@@ -15,14 +17,17 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.example.core.model.PrintEntity
import com.example.design_system.util.PspJavaScriptInterface
import com.example.design_system.util.snackbar.SnackbarController
import com.example.design_system.util.snackbar.SnackbarEvent
import kotlinx.coroutines.launch
@SuppressLint("JavascriptInterface")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PspWebView(
@@ -31,7 +36,8 @@ fun PspWebView(
modifier: Modifier = Modifier,
onWebView: (WebView) -> Unit,
onPayClick: (amount: Long, id: String?) -> Unit,
onPrintClick: () -> Unit,
onUUID: () -> Unit,
onPrintClick: (printEntities: List<PrintEntity>) -> Unit,
) {
val canGoBack = remember { mutableStateOf(false) }
@@ -40,6 +46,18 @@ fun PspWebView(
var lastBackTime by remember { mutableLongStateOf(0L) }
var isPaymentLaunching by remember { mutableStateOf(false) }
// Use rememberUpdatedState to ensure the bridge always calls the latest callbacks
val currentOnPayClick by rememberUpdatedState(onPayClick)
val currentOnPrintClick by rememberUpdatedState(onPrintClick)
val currentOnUUID by rememberUpdatedState(onUUID)
val bridge = remember {
PspJavaScriptInterface(
onPayClick = { amount, id -> currentOnPayClick(amount, id) },
onPrintClick = { entities -> currentOnPrintClick(entities) },
onUUID = { currentOnUUID() }
)
}
BackHandler {
if (canGoBack.value) {
@@ -61,7 +79,6 @@ fun PspWebView(
}
}
AndroidView(
modifier = modifier,
factory = { ctx ->
@@ -89,23 +106,7 @@ fun PspWebView(
"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 Chrome/120.0 Mobile Safari/537.36"
}
addJavascriptInterface(
object : PspJavaScriptInterface {
@JavascriptInterface
override fun pay(amount: Long, id: String?) {
onPayClick(amount, id)
}
@JavascriptInterface
override fun print(text: String) {
TODO("Not yet implemented")
}
}, "AndroidPSP"
)
addJavascriptInterface(bridge, "NativeBridge")
loadUrl(url)
@@ -1,8 +1,51 @@
package com.example.design_system.util
interface PspJavaScriptInterface {
import android.util.Log
import android.webkit.JavascriptInterface
import androidx.annotation.Keep
import com.example.core.model.PrintEntity
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
fun pay(amount: Long,id: String?)
@Keep
class PspJavaScriptInterface(
private val onPayClick: (Long, String?) -> Unit,
private val onPrintClick: (List<PrintEntity>) -> Unit,
private val onUUID: () -> Unit,
) {
fun print(text: String)
@JavascriptInterface
@Keep
fun pay(amount: Double, id: String?) {
Log.d("PspBridge", "pay called: amount=$amount, id=$id")
try {
onPayClick(amount.toLong(), id)
} catch (e: Exception) {
Log.e("PspBridge", "Error in pay callback", e)
}
}
@JavascriptInterface
@Keep
fun print(printEntitiesJson: String) {
Log.d("PspBridge", "print called: $printEntitiesJson")
try {
val type = object : TypeToken<List<PrintEntity>>() {}.type
val list: List<PrintEntity> = Gson().fromJson(printEntitiesJson, type)
onPrintClick(list)
} catch (e: Exception) {
Log.e("PspBridge", "Error parsing print entities", e)
}
}
@JavascriptInterface
@Keep
fun uuid() {
Log.d("PspBridge", "uuid called")
try {
onUUID()
} catch (e: Exception) {
Log.e("PspBridge", "Error in uuid callback", e)
}
}
}
View File
+16 -14
View File
@@ -5,24 +5,29 @@ plugins {
}
android {
namespace = "com.example.ps3"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}
namespace = "com.example.p3"
compileSdk = 36
defaultConfig {
minSdk = 24
// targetSdk is often not needed for library modules
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_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
@@ -39,10 +44,7 @@ dependencies {
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.junit)
// implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
// 2. This specifically includes the platform SDK jar
compileOnly(files("libs/platform_sdk_v2.3.706.jar"))
// Using implementation to bundle the JAR and avoid NoClassDefFoundError
implementation(files("libs/platform_sdk_v2.3.706.jar"))
implementation(files("libs/classes-2.1.17.jar"))
}
Binary file not shown.
View File
@@ -1,4 +1,4 @@
package com.example.ps3
package com.example.p3
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -1,4 +1,4 @@
package com.example.ps3
package com.example.p3
import android.content.Context
import android.content.Intent
@@ -6,7 +6,7 @@ import android.content.pm.PackageManager
import androidx.activity.result.ActivityResult
import com.example.core.model.PaymentResultEntity
import com.example.core.model.PaymentResultStatus
import com.example.ps3.models.IapAccountSplit
import com.example.p3.models.IapAccountSplit
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import javax.inject.Inject
@@ -0,0 +1,333 @@
package com.example.p3
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.createBitmap
import com.example.core.domain.Printer
import com.example.core.model.PrintEntity
import com.pos.sdk.printer.POIPrinterManager
import com.pos.sdk.printer.POIPrinterManager.IPrinterListener
import com.pos.sdk.printer.models.BitmapPrintLine
import com.pos.sdk.printer.models.PrintLine
import com.pos.sdk.printer.models.TextPrintLine
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
import javax.inject.Inject
class P3Printer @Inject constructor(
@param:ApplicationContext private val context: Context,
) : Printer {
companion object {
private const val TAG = "Printer"
// Printer state constants (should ideally come from the SDK if available)
private const val PRINTER_STATE_ERROR = 4
private const val DEFAULT_PRINT_GRAY = 2000
private const val DEFAULT_LINE_SPACE = 5
}
/**
* Prints a sample receipt for demonstration.
*/
fun printSampleReceipt(context: Context) {
// Use applicationContext to avoid potential memory leaks
val printerManager = initialP3Printer()
printerManager?.apply {
setPrintGray(DEFAULT_PRINT_GRAY)
setLineSpace(DEFAULT_LINE_SPACE)
// Header
addPrintLine(TextPrintLine("This is an example of a receipt", PrintLine.CENTER))
// Reusable logo bitmap
val logoBitmap = drawableToBitmap(context, android.R.drawable.btn_plus)
val logoLine = logoBitmap?.let { BitmapPrintLine(it, PrintLine.CENTER) }
logoLine?.let { addPrintLine(it) }
// Address
val addressFont = getFontPath("font")
if (addressFont.isNotEmpty()) {
setPrintFont(addressFont)
} else {
setPrintFont("/system/fonts/ComingSoon.ttf")
}
addPrintLine(
TextPrintLine(
"Floor ** , Building **, No.*** LONG DONG Avenue, Pudong New District, Shanghai, China",
PrintLine.LEFT,
20
)
)
// Transaction Details
val detailFont = getFontPath("font")
if (detailFont.isNotEmpty()) {
setPrintFont(detailFont)
} else {
setPrintFont("/system/fonts/DroidSansMono.ttf")
}
// addPrintLine(createPrintRow("24 June 2025", " Assistant 6", "815002", size = 18))
// addPrintLine(createPrintRow("Item", "Quantity", "Price", size = 24, isBold = true))
// addPrintLine(createPrintRow("Tomato", "1", "$2.08", size = 24))
// addPrintLine(createPrintRow("Orange", "1", "$1.06", size = 24))
//
// Footer
addPrintLine(TextPrintLine("Total $3.14", PrintLine.RIGHT))
addPrintLine(TextPrintLine(""))
logoLine?.let { addPrintLine(it) }
addPrintLine(TextPrintLine(""))
addPrintLine(
TextPrintLine(
"Did you know you could have earned Rewards points on this purchase?",
PrintLine.CENTER
)
)
addPrintLine(
TextPrintLine(
"Simply sign up today for a Membership Card!",
PrintLine.CENTER
)
)
logoLine?.let { addPrintLine(it) }
addPrintLine(TextPrintLine(" ", 0, 100))
val listener = object : IPrinterListener {
override fun onStart() {
Log.d(TAG, "Print started")
}
override fun onFinish() {
Log.d(TAG, "Print finished")
close()
}
override fun onError(code: Int, msg: String?) {
Log.e(TAG, "Print error - code: $code, message: $msg")
close()
}
}
beginPrint(listener)
}
}
private fun initialP3Printer(): POIPrinterManager? {
val printerManager = POIPrinterManager(context.applicationContext)
printerManager.open()
val state = printerManager.printerState
Log.d(TAG, "Printer state = $state")
if (state == PRINTER_STATE_ERROR) {
Log.e(TAG, "Printer is in an error state ($state). Aborting print.")
printerManager.close()
return null
}
return printerManager
}
/**
* Creates a row with left, center, and right aligned text.
*/
private fun createPrintRow(
label: String,
value: String,
size: Int,
isBold: Boolean = false
): List<TextPrintLine> {
return if (value.hasPersianLetters()) {
listOf(
TextPrintLine(label, PrintLine.LEFT, size, true),
TextPrintLine(value, PrintLine.RIGHT, size, isBold)
)
} else {
listOf(
TextPrintLine(label, PrintLine.LEFT, size, true),
TextPrintLine(value, PrintLine.LEFT, size, isBold)
)
}
}
private fun getFontPath(resourceName: String): String {
return try {
// Find resource ID by name (e.g., "font") in the "font" resource folder
val resId = context.resources.getIdentifier(resourceName, "font", context.packageName)
if (resId == 0) {
Log.e(TAG, "Font resource not found: $resourceName")
return ""
}
val fileName = "$resourceName.ttf"
val fontFile = File(context.filesDir, fileName)
if (!fontFile.exists()) {
context.resources.openRawResource(resId).use { input ->
fontFile.outputStream().use { output ->
input.copyTo(output)
}
}
}
fontFile.absolutePath
} catch (e: Exception) {
Log.e(TAG, "Error loading font from resources: $resourceName", e)
""
}
}
override fun print(
printEntities: List<PrintEntity>,
@DrawableRes icon: Int
) {
val printerManager = initialP3Printer()
printerManager?.apply {
setPrintGray(DEFAULT_PRINT_GRAY)
setLineSpace(DEFAULT_LINE_SPACE)
// Reusable logo bitmap
val logoBitmap = drawableToBitmap(context, icon, 200)
if (logoBitmap != null) {
val logoLine = BitmapPrintLine(logoBitmap, PrintLine.CENTER)
addPrintLine(logoLine)
addPrintLine(TextPrintLine(" ", 0, 10))
}
val customFontPath = getFontPath("font")
if (customFontPath.isNotEmpty()) {
setPrintFont(customFontPath)
} else {
setPrintFont("/system/fonts/DroidSansMono.ttf") // Fallback
}
val divider = drawableToBitmap(context, com.example.core.R.drawable.horizontal_line_svgrepo_com__1_)
printEntities.forEachIndexed { index, printEntity ->
// Header
printEntity.title?.let {title->
addPrintLine(
TextPrintLine(
title,
if (title.hasPersianLetters()) PrintLine.LEFT else PrintLine.RIGHT
)
)
}
printEntity.items?.let {items->
items.forEach { itemEntity ->
addPrintLine(
createPrintRow(
label = itemEntity.label,
value = itemEntity.value,
size = 16,
)
)
}
}
if (divider != null && printEntities.lastIndex > index) {
val dividerLine = BitmapPrintLine(divider, PrintLine.CENTER)
addPrintLine(dividerLine)
}
}
addPrintLine(TextPrintLine(" ", 0, 60))
val listener = object : IPrinterListener {
override fun onStart() {
Log.d(TAG, "Print started")
}
override fun onFinish() {
Log.d(TAG, "Print finished")
close()
}
override fun onError(code: Int, msg: String?) {
Log.e(TAG, "Print error - code: $code, message: $msg")
close()
}
}
beginPrint(listener)
}
}
// private fun fixForPrinter(input: String): String {
// val LRM = '\u200E' // کنترل جهت
//
// return input
// .toPersianDigits() // همونی که قبل دادم
// .replace(Regex("(\\d+)")) { matchResult ->
// "$LRM${matchResult.value}$LRM"
// }
// }
private fun String.hasPersianLetters(): Boolean {
return Regex("[\\u0621-\\u0628\\u062A-\\u063A\\u0641-\\u0642\\u0644-\\u0648\\u067E\\u0686\\u0698\\u06A9\\u06AF\\u06CC\\u0647]").containsMatchIn(
this
)
}
private fun drawableToBitmap(context: Context, @DrawableRes drawableId: Int): Bitmap? {
val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val bitmap = createBitmap(
drawable.intrinsicWidth.coerceAtLeast(1),
drawable.intrinsicHeight.coerceAtLeast(1)
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
private fun drawableToBitmap(
context: Context,
@DrawableRes drawableId: Int,
maxWidth: Int = 300
): Bitmap? {
val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
val original = if (drawable is BitmapDrawable) {
drawable.bitmap
} else {
val bitmap = createBitmap(
drawable.intrinsicWidth.coerceAtLeast(1),
drawable.intrinsicHeight.coerceAtLeast(1)
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
// 👇 scale با حفظ نسبت
val ratio = maxWidth.toFloat() / original.width
val width = maxWidth
val height = (original.height * ratio).toInt()
return Bitmap.createScaledBitmap(original, width, height, true)
}
}
@@ -1,4 +1,4 @@
package com.example.ps3
package com.example.p3
import android.content.Intent
import androidx.activity.result.ActivityResult
@@ -8,7 +8,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
class Ps3Service @Inject constructor(
class P3Service @Inject constructor(
private val iapPosService: IapPosService,
private val printerHelper: PrinterHelper
) : PspService {
@@ -21,9 +21,8 @@ class Ps3Service @Inject constructor(
}
override fun print(lines: List<String>, onMessage: (String) -> Unit) {
// printerHelper.printText()
printerHelper.printLines(lines) { status ->
onMessage("PS3 Printer: $status")
onMessage("P3 Printer: $status")
}
}
@@ -1,4 +1,4 @@
package com.example.ps3
package com.example.p3
import android.device.PrinterManager
import android.graphics.Bitmap
@@ -1,7 +1,7 @@
package com.example.ps3.di
package com.example.p3.di
import android.content.Context
import com.example.ps3.IapPosService
import com.example.p3.IapPosService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -11,7 +11,7 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object Ps3Module {
object P3Module {
@Provides
@Singleton
@@ -1,4 +1,4 @@
package com.example.ps3.models
package com.example.p3.models
data class IapAccountSplit(
val index: Int,
Binary file not shown.
@@ -1,4 +1,4 @@
package com.example.ps3
package com.example.p3
import org.junit.Test
@@ -0,0 +1,20 @@
package com.example.ps4
import android.content.Context
import androidx.annotation.DrawableRes
import com.example.core.domain.Printer
import com.example.core.model.PrintEntity
import javax.inject.Inject
class P4Printer @Inject constructor(
private val context: Context,
) : Printer {
override fun print(
printEntities: List<PrintEntity>,
@DrawableRes icon: Int
) {
}
}
+3 -2
View File
@@ -24,8 +24,9 @@ dependencyResolutionManagement {
rootProject.name = "PSP"
include(":app")
include(":tis_sep")
include(":ps3")
include(":tis_app")
include(":p3")
include(":ps4")
include(":core")
include(":design_system")
include(":stage_app")
+85
View File
@@ -0,0 +1,85 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)}
android {
namespace = "com.example.stage_app"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}
defaultConfig {
applicationId = "com.example.stage_app"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions.add("module")
productFlavors {
create("p3") {
dimension = "module"
}
create("ps4") {
dimension = "module"
}
create("standard") {
dimension = "module"
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
compose = true
buildConfig = true
}
}
dependencies {
implementation(project(":core"))
implementation(project(":design_system"))
"p3Implementation"(project(":p3"))
"ps4Implementation"(project(":ps4"))
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
testImplementation(libs.junit)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.junit)
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)
}
@@ -0,0 +1,24 @@
package com.example.stage_app
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.stage_app", appContext.packageName)
}
}
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
<package android:name="ir.co.pna.pos" />
<intent>
<action android:name="ir.co.pna.pos.view.cart.IAPCActivity" />
</intent>
</queries>
<uses-permission android:name="android.permission.PRINTER" />
<uses-permission android:name="com.pos.permission.PRINTER" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".PspApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PSP"
android:usesCleartextTraffic="true">
<uses-library android:name="android.device" android:required="false" />
<activity
android:name=".StageMainActivity"
android:exported="true"
android:theme="@style/Theme.PSP">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,7 @@
package com.example.stage_app
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class PspApplication : Application()
@@ -0,0 +1,69 @@
package com.example.stage_app
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import com.example.design_system.theme.tis.TisTheme
import com.example.design_system.util.ObserveAsEvents
import com.example.design_system.util.snackbar.SnackbarController
import com.example.stage_app.screen.StageWebViewScreen
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
@AndroidEntryPoint
class StageMainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TisTheme {
val scope = rememberCoroutineScope()
val snackbarHostState = remember {
SnackbarHostState()
}
ObserveAsEvents(
flow = SnackbarController.events,
snackbarHostState
) { event ->
scope.launch {
snackbarHostState.currentSnackbarData?.dismiss()
val result = snackbarHostState.showSnackbar(
message = event.message,
actionLabel = event.action?.name,
duration = event.duration
)
if (result == SnackbarResult.ActionPerformed) {
event.action?.action?.invoke()
}
}
}
Scaffold(
snackbarHost = {
SnackbarHost(
hostState = snackbarHostState
)
}
) { paddingValues ->
StageWebViewScreen(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
)
}
}
}
}
}
@@ -0,0 +1,73 @@
package com.example.stage_app.screen
import android.provider.Settings
import android.webkit.WebView
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.design_system.PspWebView
import com.example.stage_app.viewmodel.StageViewModel
private const val tisIp = "192.168.128.73:5000"
@Composable
fun StageWebViewScreen(
modifier: Modifier = Modifier,
viewModel: StageViewModel = hiltViewModel()
) {
val context = LocalContext.current
var webView: WebView? by remember { mutableStateOf(null) }
var currentPaymentId by rememberSaveable { mutableStateOf<String?>(null) }
// launcher to handle POS app result
val posLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
val paymentResultEntity = viewModel.paymentResult(
id = currentPaymentId,
activityResult = result
)
webView?.post {
webView?.evaluateJavascript(
viewModel.postPaymentResult(paymentResultEntity = paymentResultEntity),
null
)
}
}
PspWebView(
url = tisIp,
webView = webView,
modifier = modifier,
onWebView = { updatedWebView -> webView = updatedWebView },
onPayClick = { amount: Long, id: String? ->
currentPaymentId = id
viewModel.pay(amount)
.also(posLauncher::launch)
},
onPrintClick = { printEntities ->
viewModel.print(printEntities)
},
onUUID = {
val androidId = Settings
.Secure
.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
viewModel.sendAndroidId(
androidId
)
}
)
}
@@ -0,0 +1,72 @@
package com.example.stage_app.viewmodel
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.lifecycle.ViewModel
import com.example.core.domain.AppConfig
import com.example.core.domain.PaymentResult
import com.example.core.domain.Printer
import com.example.core.domain.PspService
import com.example.core.model.PaymentResultEntity
import com.example.core.model.PrintEntity
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class StageViewModel @Inject constructor(
private val pspService: PspService,
private val paymentResult: PaymentResult,
private val printer: Printer,
private val appConfig: AppConfig
) : ViewModel() {
fun postPaymentResult(paymentResultEntity: PaymentResultEntity?): String {
return paymentResult.post(paymentResultEntity)
}
fun pay(amount: Long): Intent = pspService.pay(amount)
fun paymentResult(
id: String?,
activityResult: ActivityResult
): PaymentResultEntity? {
return pspService.decodePosResponse(
id = id,
activityResult = activityResult
)
}
private val mock = listOf<PrintEntity>(
PrintEntity(
title = "اطلاعات مشتری",
items = listOf(
PrintEntity.ItemEntity("نام مشتری", "علی رضایی"),
PrintEntity.ItemEntity("شماره تماس", "09123456789"),
PrintEntity.ItemEntity("آدرس", "تهران، خیابان ولیعصر، پلاک ۱۲۳")
),
type = PrintEntity.Type.CARD
),
PrintEntity(
title = "اطلاعات پرداخت",
items = listOf(
PrintEntity.ItemEntity("روش پرداخت", "کارت به کارت"),
PrintEntity.ItemEntity("شماره پیگیری", "7845129630"),
PrintEntity.ItemEntity("تاریخ", "1405/02/14")
),
type = PrintEntity.Type.CARD
),
)
fun print(printEntities: List<PrintEntity>) {
printer.print(
printEntities = printEntities,
icon = android.R.drawable.btn_star
)
}
fun sendAndroidId(id: String) {
appConfig.androidId(id)
}
}
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.PSP" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">stage_app</string>
</resources>
+16
View File
@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.PSP" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
@@ -0,0 +1,20 @@
package com.example.tis_app.di
import com.example.core.domain.Printer
import com.example.core.domain.PspService
import com.example.p3.P3Printer
import com.example.p3.P3Service
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
abstract class PspModule {
@Binds
abstract fun bindPspService(impl: P3Service): PspService
@Binds
abstract fun bindPrinter(impl: P3Printer): Printer
}
@@ -2,6 +2,7 @@ package com.example.tis_sep.di
import com.example.core.PspService
import com.example.ps4.Ps4Service
import com.example.ps4.P4Printer
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@@ -12,4 +13,8 @@ import dagger.hilt.components.SingletonComponent
abstract class PspModule {
@Binds
abstract fun bindPspService(impl: Ps4Service): PspService
@Binds
abstract fun bindPrinter(impl: P4Printer): Printer
}
@@ -0,0 +1,17 @@
package com.example.stage_app
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
+1
View File
@@ -0,0 +1 @@
/build
@@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.tis_sep"
namespace = "com.example.tis_app"
compileSdk {
version = release(36) {
minorApiLevel = 1
@@ -14,7 +14,7 @@ android {
}
defaultConfig {
applicationId = "com.example.tis_sep"
applicationId = "com.example.tis_app"
minSdk = 24
targetSdk = 36
versionCode = 1
@@ -25,7 +25,7 @@ android {
flavorDimensions.add("module")
productFlavors {
create("ps3") {
create("p3") {
dimension = "module"
}
create("ps4") {
@@ -58,11 +58,7 @@ android {
dependencies {
implementation(project(":core"))
implementation(project(":design_system"))
"ps3Implementation"(project(":ps3"))
"ps3CompileOnly"(files("../ps3/libs/platform_sdk_v2.3.706.jar"))
// Provide the library for compilation only in the ps3 flavor
// "ps3CompileOnly"(files("${rootProject.projectDir}/external_sdks/platform_sdk_v2.3.706.jar"))
"p3Implementation"(project(":p3"))
"ps4Implementation"(project(":ps4"))
+21
View File
@@ -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
@@ -1,4 +1,4 @@
package com.example.tis_sep
package com.example.tis_app
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -1,4 +1,4 @@
package com.example.tis_sep
package com.example.tis_app
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@@ -1,4 +1,4 @@
package com.example.tis_sep
package com.example.tis_app
import android.os.Bundle
import androidx.activity.ComponentActivity
@@ -15,7 +15,7 @@ import androidx.compose.ui.Modifier
import com.example.design_system.theme.tis.TisTheme
import com.example.design_system.util.ObserveAsEvents
import com.example.design_system.util.snackbar.SnackbarController
import com.example.tis_sep.screen.TisWebViewScreen
import com.example.tis_app.screen.TisWebViewScreen
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
@@ -55,6 +55,8 @@ class TisMainActivity : ComponentActivity() {
)
}
) { paddingValues ->
TisWebViewScreen(
modifier = Modifier
.fillMaxSize()
@@ -1,5 +1,6 @@
package com.example.tis_sep.screen
package com.example.tis_app.screen
import android.provider.Settings
import android.webkit.WebView
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -10,16 +11,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.design_system.PspWebView
import com.example.tis_sep.viewmodel.WebViewModel
import com.example.tis_app.viewmodel.WebViewModel
private const val tisIp = "194.59.214.243:8090"
@Composable
fun TisWebViewScreen(
modifier: Modifier = Modifier,
viewModel: WebViewModel = hiltViewModel()
) {
val context = LocalContext.current
var webView: WebView? by remember { mutableStateOf(null) }
var currentPaymentId by rememberSaveable { mutableStateOf<String?>(null) }
@@ -42,7 +45,7 @@ fun TisWebViewScreen(
}
PspWebView(
url = "192.168.128.73:5000",
url = tisIp,
webView = webView,
modifier = modifier,
onWebView = { updatedWebView -> webView = updatedWebView },
@@ -51,6 +54,18 @@ fun TisWebViewScreen(
viewModel.pay(amount)
.also(posLauncher::launch)
},
onPrintClick = {},
onPrintClick = { printEntities ->
viewModel.print(printEntities)
},
onUUID = {
val androidId = Settings
.Secure
.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
viewModel.sendAndroidId(
androidId
)
}
)
}
@@ -1,18 +1,24 @@
package com.example.tis_sep.viewmodel
package com.example.tis_app.viewmodel
import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.lifecycle.ViewModel
import com.example.core.domain.AppConfig
import com.example.core.domain.PaymentResult
import com.example.core.domain.Printer
import com.example.core.domain.PspService
import com.example.core.model.PaymentResultEntity
import com.example.core.model.PrintEntity
import com.example.tis_app.R
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class WebViewModel @Inject constructor(
private val pspService: PspService,
private val paymentResult: PaymentResult
private val paymentResult: PaymentResult,
private val printer: Printer,
private val appConfig: AppConfig
) : ViewModel() {
@@ -31,4 +37,14 @@ class WebViewModel @Inject constructor(
activityResult = activityResult
)
}
fun print(printEntities : List<PrintEntity>) {
printer.print(
printEntities = printEntities,
icon = R.drawable.tis_icon
)
}
fun sendAndroidId(id : String) {
appConfig.androidId(id)
}
}
@@ -0,0 +1,20 @@
<!--
~ Copyright (C) 2026 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="#FF000000" android:pathData="M17.6,11.48 L19.44,8.3a0.63,0.63 0,0 0,-1.09 -0.63l-1.88,3.24a11.43,11.43 0,0 0,-8.94 0L5.65,7.67a0.63,0.63 0,0 0,-1.09 0.63L6.4,11.48A10.81,10.81 0,0 0,1 20L23,20A10.81,10.81 0,0 0,17.6 11.48ZM7,17.25A1.25,1.25 0,1 1,8.25 16,1.25 1.25,0 0,1 7,17.25ZM17,17.25A1.25,1.25 0,1 1,18.25 16,1.25 1.25,0 0,1 17,17.25Z"/>
</vector>
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group android:scaleX="1.42"
android:scaleY="1.42"
android:translateX="-22.68"
android:translateY="-22.68">
<path android:fillColor="#FFFFFF"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#FFFFFF" android:pathData="M9,0L9,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,0L19,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M29,0L29,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M39,0L39,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M49,0L49,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M59,0L59,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M69,0L69,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M79,0L79,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M89,0L89,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M99,0L99,108"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,9L108,9"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,19L108,19"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,29L108,29"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,39L108,39"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,49L108,49"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,59L108,59"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,69L108,69"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,79L108,79"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,89L108,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M0,99L108,99"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,29L89,29"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,39L89,39"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,49L89,49"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,59L89,59"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,69L89,69"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M19,79L89,79"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M29,19L29,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M39,19L39,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M49,19L49,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M59,19L59,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M69,19L69,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#FFFFFF" android:pathData="M79,19L79,89"
android:strokeColor="#FFFFFF" android:strokeWidth="0.8"/>
</group>
</vector>
+144
View File
@@ -0,0 +1,144 @@
<!--
~ Copyright (C) 2026 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="237dp"
android:height="68dp"
android:viewportWidth="237"
android:viewportHeight="68">
<path
android:pathData="M28.5,22.75C32,23 32,23 34,25C34.29,26.85 34.29,26.85 34.34,29.06C34.37,29.86 34.4,30.66 34.43,31.48C34.48,33.16 34.52,34.84 34.57,36.52C34.6,37.32 34.63,38.12 34.66,38.94C34.68,40.03 34.68,40.03 34.71,41.15C34.85,43.25 34.85,43.25 37,45C40.31,41.69 39.4,36.71 39.48,32.21C39.68,26.41 39.68,26.41 41.34,24.28C43,23 43,23 46,22.56C49,23 49,23 50.68,24.45C52.68,28.32 52.32,31.96 52.19,36.25C52.17,37.51 52.17,37.51 52.15,38.8C52.11,40.86 52.06,42.93 52,45C53.32,44.67 54.64,44.34 56,44C56.01,43.43 56.03,42.87 56.04,42.29C56.12,39.73 56.22,37.18 56.31,34.63C56.34,33.74 56.36,32.85 56.38,31.93C56.42,31.08 56.45,30.23 56.49,29.35C56.51,28.57 56.54,27.78 56.57,26.97C57,25 57,25 60,23C63.06,22.75 63.06,22.75 66,23C68.44,25.44 68.29,26.21 68.41,29.57C68.47,30.78 68.47,30.78 68.52,32.02C68.56,33.04 68.59,34.07 68.63,35.13C68.75,38.38 68.87,41.64 69,45C81.38,44.5 81.38,44.5 94,44C94.21,38.21 94.21,38.21 94.39,32.42C94.44,31.34 94.44,31.34 94.49,30.23C94.53,29.12 94.53,29.12 94.57,27.99C95.11,25.49 96,24.56 98,23C100.82,22.85 103.2,23.46 106,24C107.51,25.51 107.2,26.89 107.28,29C107.34,30.21 107.34,30.21 107.39,31.44C107.46,33.15 107.54,34.85 107.61,36.56C107.66,37.77 107.66,37.77 107.72,39C107.75,39.74 107.78,40.49 107.81,41.25C107.82,43.03 107.82,43.03 109,44C111.38,44.09 113.72,44.12 116.1,44.1C116.81,44.1 117.52,44.09 118.25,44.09C120.52,44.09 122.79,44.08 125.06,44.06C126.6,44.06 128.14,44.05 129.67,44.05C133.45,44.04 137.22,44.02 141,44C142.12,41.76 142.19,40.35 142.32,37.87C142.36,37.06 142.4,36.26 142.44,35.43C142.48,34.58 142.52,33.74 142.56,32.88C142.63,31.6 142.63,31.6 142.69,30.3C142.8,28.2 142.9,26.1 143,24C146.01,22.49 148.68,22.82 152,23C155.33,26.33 154.37,30.67 154.38,35.19C154.4,36.12 154.42,37.06 154.45,38.02C154.48,44.23 153.79,48.01 150,53C145.51,56.61 141.37,56.42 135.87,56.36C134.56,56.36 134.56,56.36 133.22,56.36C131.38,56.36 129.54,56.35 127.7,56.34C124.88,56.31 122.06,56.32 119.24,56.32C117.45,56.32 115.66,56.31 113.87,56.3C113.02,56.3 112.18,56.3 111.31,56.3C107.38,56.26 105.35,56.23 102,54C101.49,54.33 100.98,54.65 100.45,54.99C97.13,56.36 94.34,56.24 90.75,56.23C90.05,56.23 89.36,56.23 88.64,56.23C87.18,56.23 85.71,56.22 84.25,56.21C82.01,56.19 79.77,56.19 77.53,56.19C66.49,56.15 66.49,56.15 62,55C61.01,55.49 61.01,55.49 60,56C58.21,56.1 56.42,56.13 54.63,56.13C53.67,56.13 52.71,56.13 51.73,56.13C49,56 49,56 46.77,55.43C44.91,54.78 44.91,54.78 43,56C40.7,56.23 38.4,56.38 36.09,56.52C33.61,56.84 33.61,56.84 32,60C31.34,60 30.68,60 30,60C29.67,60.99 29.34,61.98 29,63C24.4,65.77 19.14,65.37 13.94,65.38C12.59,65.41 12.59,65.41 11.22,65.45C4.68,65.48 4.68,65.48 1.38,63.14C0,61 0,61 0.06,58.38C1,56 1,56 4,54C6.71,53.72 9.35,53.54 12.06,53.44C17.19,53.43 17.19,53.43 22,52C22.02,51.19 22.04,50.39 22.06,49.55C22.16,45.91 22.26,42.27 22.38,38.63C22.42,36.72 22.42,36.72 22.47,34.77C22.51,33.56 22.55,32.35 22.59,31.1C22.62,29.98 22.65,28.86 22.68,27.71C23.18,23.44 24.29,23.05 28.5,22.75Z"
android:fillColor="#0A774D"/>
<path
android:pathData="M184,0C186.89,-0.12 188.37,-0.09 191,1C194.04,4.06 196.47,7.52 199,11C202.48,15.62 202.48,15.62 207,19C207,19.99 207,20.98 207,22C207.99,22.33 208.98,22.66 210,23C211.87,25.07 211.87,25.07 213.88,27.69C216.17,30.67 218.47,33.52 221.06,36.25C223,39 223,39 223.13,42.06C221.72,45.72 220.03,47.54 217,50C214.69,50.81 214.69,50.81 213,51C213.1,51.72 213.21,52.44 213.31,53.19C212.87,57.17 211.01,58.48 208,61C206.51,61.49 206.51,61.49 205,62C203.77,65.01 203.77,65.01 203,68C199.37,68 195.74,68 192,68C194.03,63.95 195.89,61.05 200,59C201.01,57.25 202.01,55.49 202.99,53.72C204.12,51.79 205.19,50.34 207,49C207.99,49 208.98,49 210,49C210.62,45.56 211,42.51 211,39C209.71,37.09 209.71,37.09 208,35.38C205,32.19 205,32.19 205,30C204.34,30 203.68,30 203,30C201.77,28.31 200.55,26.61 199.37,24.88C197.89,22.75 197.89,22.75 195.25,20.25C193,18 193,18 192,15C191.34,15 190.68,15 190,15C187.27,12.27 184.97,9.32 183,6C183.06,2.44 183.06,2.44 184,0Z"
android:fillColor="#11754D"/>
<path
android:pathData="M201,0C203.63,0.38 203.63,0.38 206,1C206.33,1.99 206.66,2.98 207,4C207.66,4 208.32,4 209,4C209.34,5.39 209.34,5.39 209.69,6.81C211.25,10.62 212.51,11.04 216,13C217.02,14.65 218.03,16.32 219,18C219.99,18.33 220.98,18.66 222,19C227,24.47 227,24.47 227,27C227.66,27 228.32,27 229,27C233,31.47 233,31.47 233,34C234.32,34.66 235.64,35.32 237,36C237.49,43.22 237.49,43.22 235.13,47C233,49 233,49 231,49C230.77,49.7 230.55,50.39 230.32,51.11C228.71,54.63 226.46,57.31 223.94,60.19C220.63,63.86 220.63,63.86 218,68C214.7,68 211.4,68 208,68C209,64 209,64 211,61C211.66,61 212.32,61 213,61C213.37,60.19 213.37,60.19 213.75,59.36C215.28,56.47 217.09,53.9 219,51.25C219.7,50.27 220.4,49.28 221.13,48.27C221.74,47.52 222.36,46.77 223,46C223.66,46 224.32,46 225,46C225.33,44.68 225.66,43.36 226,42C226.66,42 227.32,42 228,42C227.53,41.24 227.05,40.47 226.56,39.69C225,37 225,37 224,34C223.34,34 222.68,34 222,34C220.27,32.14 220.27,32.14 218.38,29.69C217.74,28.89 217.11,28.09 216.46,27.26C215,25 215,25 215,22C213.68,22 212.36,22 211,22C208.75,19.75 208.75,19.75 207,17C207,15.68 207,14.36 207,13C206.24,12.75 205.47,12.51 204.69,12.25C201.68,10.85 200.57,9.9 199,7C198.69,4.38 198.69,4.38 199,2C199.66,1.34 200.32,0.68 201,0Z"
android:fillColor="#10744D"/>
<path
android:pathData="M161,19C163.31,19 163.31,19 166,20C167.29,21.42 168.58,22.85 169.73,24.38C171.15,26.27 171.15,26.27 173.75,28.69C176,31 176,31 177,34C177.99,34.33 178.98,34.66 180,35C181.47,42.1 181.47,42.1 179.19,46.5C178.1,47.74 178.1,47.74 177,49C176.34,49 175.68,49 175,49C174.77,49.7 174.55,50.4 174.31,51.13C172.66,54.74 170.71,57.09 168,60C167.34,60 166.68,60 166,60C165.73,60.76 165.46,61.53 165.19,62.31C164,65 163.3,66.23 161,68C158.52,68.29 158.52,68.29 155.81,68.19C154.91,68.16 154.01,68.13 153.08,68.11C152.39,68.07 151.71,68.04 151,68C152.73,63.96 155.46,60.71 158.19,57.31C159.18,56.07 160.17,54.82 161.16,53.57C161.88,52.66 161.88,52.66 162.62,51.74C164.49,49.38 166.34,47 168.19,44.63C168.74,43.92 169.29,43.21 169.86,42.48C170.23,41.99 170.61,41.5 171,41C169.45,37.53 167.38,34.77 165,31.81C164.3,30.93 163.6,30.05 162.88,29.14C161.18,26.93 161.18,26.93 159,26C158.88,23.63 158.88,23.63 159,21C159.66,20.34 160.32,19.68 161,19Z"
android:fillColor="#57BF7A"/>
<path
android:pathData="M176.56,18.94C177.77,18.97 177.77,18.97 179,19C179.27,19.64 179.54,20.28 179.81,20.94C180.89,23.33 180.89,23.33 184,24C185.94,26.18 185.94,26.18 188,28.88C190.63,32.39 190.63,32.39 193.75,35.45C195,37 195,37 195.6,39.66C194.69,44.71 191.71,47.61 188.38,51.38C187.13,52.82 185.89,54.26 184.65,55.71C184.03,56.42 183.42,57.12 182.79,57.84C180.5,60.6 178.5,63.52 176.53,66.5C174.45,68.53 172.97,68.48 170.13,68.5C168.75,68.37 167.37,68.22 166,68C166.61,64.04 168.6,61.45 170.94,58.25C171.65,57.27 172.36,56.28 173.09,55.27C174.04,54.14 174.04,54.14 175,53C175.66,53 176.32,53 177,53C177.33,51.68 177.66,50.36 178,49C178.66,49 179.32,49 180,49C180.27,48.44 180.54,47.89 180.81,47.31C182.1,44.8 183.54,42.41 185,40C183.56,38.23 182.13,36.46 180.69,34.69C179.89,33.7 179.09,32.72 178.26,31.7C176.17,29.09 176.17,29.09 174.22,27.34C173,26 173,26 172.75,22.88C173.09,19.02 173.09,19.02 176.56,18.94Z"
android:fillColor="#54C079"/>
<path
android:pathData="M188,19C190.09,18.55 190.09,18.55 193,19C195.53,21.14 197.54,23.68 199.63,26.25C200.2,26.93 200.77,27.61 201.36,28.32C202.93,30.2 204.48,32.08 206,34C206.56,34.69 207.12,35.37 207.69,36.08C209,38 209,38 208.9,40.42C207.85,43.44 206.42,45.7 204.56,48.31C203.95,49.2 203.33,50.08 202.69,50.99C202.13,51.65 201.58,52.32 201,53C200.34,53 199.68,53 199,53C199,53.99 199,54.98 199,56C197.36,57.69 195.69,59.35 194,61C193.26,62.27 192.53,63.54 191.81,64.81C190.6,66.62 190.6,66.62 189,68C185.59,68.6 182.42,68.42 179,68C180.21,64.56 181.39,62.5 184,60C184.33,59.01 184.66,58.02 185,57C186.64,55.3 188.31,53.64 190,52C191.65,50.17 193.28,48.32 194.88,46.44C195.68,45.51 196.48,44.58 197.3,43.62C199.27,41.19 199.27,41.19 198.76,38.66C198.51,38.11 198.26,37.56 198,37C197.34,37 196.68,37 196,37C192,32.53 192,32.53 192,30C191.01,29.67 190.02,29.34 189,29C187.19,26.75 187.19,26.75 186,24C186.81,21.19 186.81,21.19 188,19Z"
android:fillColor="#54BF78"/>
<path
android:pathData="M8,4C10.38,4.31 10.38,4.31 13,5C15,8 15,8 15,12C16.32,12 17.64,12 19,12C19.66,10.68 20.32,9.36 21,8C21.66,8 22.32,8 23,8C23,7.34 23,6.68 23,6C27.75,6.88 27.75,6.88 30,8C30,9.32 30,10.64 30,12C32.97,12 35.94,12 39,12C38.5,11.01 38.5,11.01 38,10C36.02,10 34.04,10 32,10C32,8.68 32,7.36 32,6C33.65,5.97 35.29,5.95 36.94,5.94C38.31,5.92 38.31,5.92 39.71,5.9C42,6 42,6 43,7C43.04,8.67 43.04,10.33 43,12C44.98,12 46.96,12 49,12C49,11.34 49,10.68 49,10C47.68,10 46.36,10 45,10C45,8.68 45,7.36 45,6C48.96,6 52.92,6 57,6C57,7.32 57,8.64 57,10C55.68,10 54.36,10 53,10C53,10.66 53,11.32 53,12C58.43,11.62 60.33,9.87 64,6C64.99,6 65.98,6 67,6C67,6.66 67,7.32 67,8C67.99,8.33 68.98,8.66 70,9C70.94,10.81 70.94,10.81 71,13C69.5,15.31 69.5,15.31 67,17C64.66,16.71 62.33,16.36 60,16C56.87,15.85 53.75,15.86 50.63,15.88C49.76,15.88 48.89,15.88 48,15.88C41.44,15.92 34.92,16.21 28.38,16.73C24.98,16.99 22.27,16.89 19,16C16.79,15.96 14.58,15.96 12.38,16C6.22,16.11 6.22,16.11 4,15C4,14.01 4,13.02 4,12C6.64,12 9.28,12 12,12C11.01,10.58 11.01,10.58 10,9.13C8,6 8,6 8,4ZM23,11C23.33,11.66 23.66,12.32 24,13C24.99,12.67 25.98,12.34 27,12C25.68,11.67 24.36,11.34 23,11ZM64,11C64,11.66 64,12.32 64,13C64.99,12.67 65.98,12.34 67,12C66.01,11.67 65.02,11.34 64,11Z"
android:fillColor="#19704E"/>
<path
android:pathData="M119.94,28.88C121.29,28.91 122.65,28.95 124,29C125.73,29 127.46,28.99 129.19,28.93C129.97,28.91 130.76,28.89 131.56,28.88C132.74,28.84 132.74,28.84 133.94,28.8C136,29 136,29 138,31C138.32,35.34 138.45,37.33 136,41C133.97,41.37 133.97,41.37 131.5,41.36C130.61,41.36 129.73,41.36 128.81,41.36C127.42,41.34 127.42,41.34 126,41.31C125.07,41.32 124.14,41.32 123.19,41.32C116.27,41.27 116.27,41.27 114,39C113.73,37.13 113.73,37.13 113.75,35C113.74,34.3 113.74,33.6 113.73,32.88C114.3,28.85 116.18,28.99 119.94,28.88Z"
android:fillColor="#10734D"/>
<path
android:pathData="M70,57C72.06,56.71 72.06,56.71 74.44,56.81C75.61,56.85 75.61,56.85 76.81,56.89C77.53,56.93 78.25,56.96 79,57C80.74,57 82.48,56.99 84.23,56.96C85.02,56.96 85.81,56.95 86.63,56.94C87.42,56.93 88.22,56.91 89.04,56.9C91,57 91,57 92,58C92.07,59.69 92.08,61.38 92.06,63.06C92.05,63.98 92.04,64.9 92.04,65.85C92.02,66.56 92.01,67.27 92,68C88.19,68.03 84.38,68.05 80.56,68.06C79.47,68.07 78.39,68.08 77.26,68.09C76.23,68.09 75.19,68.09 74.13,68.1C72.69,68.11 72.69,68.11 71.22,68.11C69,68 69,68 68,67C67.64,60.54 67.64,60.54 70,57Z"
android:fillColor="#0D744C"/>
<path
android:pathData="M192,35C195,37 195,37 195.6,39.6C194.7,44.69 191.74,47.57 188.38,51.38C187.13,52.82 185.89,54.26 184.65,55.71C183.73,56.77 183.73,56.77 182.79,57.84C180.5,60.6 178.5,63.52 176.53,66.5C174.45,68.53 172.97,68.48 170.13,68.5C168.75,68.37 167.37,68.22 166,68C166.61,64.04 168.6,61.45 170.94,58.25C172,56.77 172,56.77 173.09,55.27C173.72,54.52 174.35,53.77 175,53C175.66,53 176.32,53 177,53C177.33,51.68 177.66,50.36 178,49C178.66,49 179.32,49 180,49C180.25,48.48 180.49,47.97 180.75,47.44C182.4,44.23 184.2,41.12 186,38C186.66,39.77 186.66,39.77 187,42C186.03,43.82 186.03,43.82 184.5,45.69C183.94,46.38 183.39,47.07 182.82,47.79C182.22,48.52 181.62,49.25 181,50C179.81,51.54 178.62,53.08 177.44,54.63C174.69,58.14 171.88,61.59 169,65C169.99,65.33 170.98,65.66 172,66C178.59,59.6 184.02,52.71 189.27,45.16C189.84,44.45 190.41,43.73 191,43C191.66,43 192.32,43 193,43C192.67,40.36 192.34,37.72 192,35Z"
android:fillColor="#60BB7E"/>
<path
android:pathData="M117,6C117.99,6.33 118.98,6.66 120,7C120,8.65 120,10.3 120,12C123.3,12 126.6,12 130,12C130.33,10.02 130.66,8.04 131,6C131.99,6 132.98,6 134,6C134.29,12.05 134.29,12.05 134,14C130.58,16.28 129.32,16.22 125.31,16.13C123.82,16.1 123.82,16.1 122.3,16.07C121.54,16.05 120.78,16.02 120,16C119.9,16.62 119.79,17.24 119.69,17.88C119,20 119,20 116,22C113.81,21.63 113.81,21.63 112,21C112.33,20.01 112.66,19.02 113,18C113.99,17.67 114.98,17.34 116,17C116.33,13.37 116.66,9.74 117,6Z"
android:fillColor="#166E4C"/>
<path
android:pathData="M83.63,5.88C84.41,5.92 85.19,5.96 86,6C86.33,6.99 86.66,7.98 87,9C85.35,9.33 83.7,9.66 82,10C82,10.66 82,11.32 82,12C83.65,12 85.3,12 87,12C87.04,14 87.04,16 87,18C85.33,19.67 83.34,19.21 81.06,19.25C80.17,19.28 79.27,19.3 78.35,19.33C77.57,19.22 76.8,19.11 76,19C73.57,15.36 73.84,13.29 74,9C74.66,8.67 75.32,8.34 76,8C76.66,8.33 77.32,8.66 78,9C78,10.98 78,12.96 78,15C78.33,14.67 78.66,14.34 79,14C78.98,13.03 78.96,12.06 78.94,11.06C79.02,7.14 79.63,6.07 83.63,5.88Z"
android:fillColor="#19714F"/>
<path
android:pathData="M198,37C198.99,37.33 199.98,37.66 201,38C200.53,42.81 198.2,45.8 195.25,49.44C194.82,49.98 194.39,50.53 193.95,51.1C191.76,53.86 189.53,56.55 187.23,59.22C184.86,61.85 184.86,61.85 184,65C185.32,65.33 186.64,65.66 188,66C188,65.34 188,64.68 188,64C188.66,64 189.32,64 190,64C190.25,63.46 190.49,62.93 190.75,62.38C194.98,54.34 194.98,54.34 199,53C199,56 199,56 196.81,58.25C195.88,59.16 194.96,60.07 194,61C193.26,62.27 192.53,63.54 191.81,64.81C190.6,66.62 190.6,66.62 189,68C185.59,68.6 182.42,68.42 179,68C180.21,64.56 181.39,62.5 184,60C184.33,59.01 184.66,58.02 185,57C186.64,55.3 188.31,53.64 190,52C191.65,50.17 193.28,48.32 194.88,46.44C195.68,45.51 196.48,44.58 197.3,43.62C199.27,41.19 199.27,41.19 198.76,38.66C198.51,38.11 198.26,37.56 198,37Z"
android:fillColor="#5EBA7D"/>
<path
android:pathData="M141,4C144.02,4.33 145.6,4.61 147.81,6.75C149.23,9.44 149.47,11 149,14C145.55,16.3 144.05,16.34 140,16C139.34,15.34 138.68,14.68 138,14C138.3,9.86 138.68,7.48 141,4ZM143,9C143,10.32 143,11.64 143,13C143.66,12.67 144.32,12.34 145,12C144.67,11.01 144.34,10.02 144,9C143.67,9 143.34,9 143,9Z"
android:fillColor="#1A714E"/>
<path
android:pathData="M93,3C94.32,3 95.64,3 97,3C97.11,5.42 97.19,7.83 97.25,10.25C97.3,11.27 97.3,11.27 97.35,12.32C97.42,15.75 97.24,17.66 95.23,20.48C93,22 93,22 89,22C89.38,20.06 89.38,20.06 90,18C90.66,17.67 91.32,17.34 92,17C92.47,13.72 92.47,13.72 92.63,9.94C92.7,8.65 92.77,7.36 92.85,6.03C92.9,5.03 92.95,4.03 93,3Z"
android:fillColor="#18724F"/>
<path
android:pathData="M172,4C174.38,4.31 174.38,4.31 177,5C179.3,8.45 179.34,9.95 179,14C177,16 177,16 173.56,16.25C170,16 170,16 167,14C167,13.34 167,12.68 167,12C169.64,12 172.28,12 175,12C174.51,11.05 174.01,10.1 173.5,9.13C172,6 172,6 172,4Z"
android:fillColor="#1D7651"/>
<path
android:pathData="M107,4C111.58,4.48 111.58,4.48 113.38,6.69C114.21,9.76 113.81,11.94 113,15C110.48,16.26 109.15,15.92 106.38,15.56C105.56,15.46 104.74,15.36 103.9,15.25C103.27,15.17 102.65,15.09 102,15C102,14.01 102,13.02 102,12C104.64,12 107.28,12 110,12C109.5,11.05 109.01,10.1 108.5,9.13C107,6 107,6 107,4Z"
android:fillColor="#1B7250"/>
<path
android:pathData="M120.56,30.94C121.7,30.96 122.83,30.98 124,31C124.13,38.75 124.13,38.75 123,41C120.63,41.19 120.63,41.19 118,41C115.71,37.56 115.74,36.03 116,32C117,31 117,31 120.56,30.94Z"
android:fillColor="#09774D"/>
<path
android:pathData="M155,4C156.32,4 157.64,4 159,4C161.68,8.02 161.16,10.24 161,15C158.48,16.26 157.15,15.92 154.38,15.56C153.56,15.46 152.74,15.36 151.9,15.25C151.27,15.17 150.65,15.09 150,15C150,14.01 150,13.02 150,12C152.64,12 155.28,12 158,12C157.51,11.24 157.01,10.47 156.5,9.69C155,7 155,7 155,4Z"
android:fillColor="#187450"/>
<path
android:pathData="M23,49C23.33,49 23.66,49 24,49C24,50.65 24,52.3 24,54C20.18,56.47 16.15,56.44 11.75,56.63C11,56.66 10.26,56.7 9.49,56.74C7.66,56.84 5.83,56.92 4,57C3.67,58.32 3.34,59.64 3,61C4.32,61.99 5.64,62.98 7,64C4.63,64.19 4.63,64.19 2,64C0,61 0,61 0.19,58.44C1,56 1,56 4,54C6.71,53.72 9.35,53.54 12.06,53.44C17.19,53.43 17.19,53.43 22,52C22.33,51.01 22.66,50.02 23,49Z"
android:fillColor="#16734E"/>
<path
android:pathData="M163,1C163.99,1 164.98,1 166,1C166,6.28 166,11.56 166,17C164.68,16.67 163.36,16.34 162,16C161.97,13.69 161.95,11.38 161.94,9.06C161.93,7.77 161.91,6.49 161.9,5.16C162,2 162,2 163,1Z"
android:fillColor="#167650"/>
<path
android:pathData="M98,1C98.99,1 99.98,1 101,1C101,6.28 101,11.56 101,17C100.01,17 99.02,17 98,17C98,11.72 98,6.44 98,1Z"
android:fillColor="#15704D"/>
<path
android:pathData="M203,61C204.13,65.75 204.13,65.75 203,68C200.36,68 197.72,68 195,68C195.91,62.69 197.53,61 203,61Z"
android:fillColor="#0D744B"/>
<path
android:pathData="M32,6C35.3,6 38.6,6 42,6C42.33,6.99 42.66,7.98 43,9C42.01,10.49 42.01,10.49 41,12C40.34,12 39.68,12 39,12C38.67,11.34 38.34,10.68 38,10C36.02,10 34.04,10 32,10C32,8.68 32,7.36 32,6Z"
android:fillColor="#1E7152"/>
<path
android:pathData="M211,50C212.25,52.49 211.78,53.41 211,56C210.34,56 209.68,56 209,56C208.34,57.32 207.68,58.64 207,60C205.68,59.67 204.36,59.34 203,59C203.34,55.83 204,55 206.44,52.81C209,51 209,51 211,50Z"
android:fillColor="#0A774C"/>
<path
android:pathData="M202,2C203.94,2.81 203.94,2.81 206,4C206.33,4.99 206.66,5.98 207,7C207.66,7 208.32,7 209,7C208.67,8.32 208.34,9.64 208,11C205.68,10.6 204.14,10.09 202.19,8.75C201,7 201,7 201.31,4.31C201.54,3.55 201.77,2.79 202,2Z"
android:fillColor="#0A774D"/>
<path
android:pathData="M176.56,18.94C177.77,18.97 177.77,18.97 179,19C179,19.66 179,20.32 179,21C177.68,21 176.36,21 175,21C175.66,23.31 176.32,25.62 177,28C177.66,28 178.32,28 179,28C180.69,29.81 180.69,29.81 182,32C181.67,32.99 181.34,33.98 181,35C179.65,33.64 178.32,32.26 177,30.88C176.26,30.11 175.51,29.34 174.75,28.55C172.62,25.45 172.58,23.69 173,20C174,19 174,19 176.56,18.94Z"
android:fillColor="#5CB879"/>
<path
android:pathData="M70,57C72.16,56.71 72.16,56.71 74.63,56.81C75.44,56.84 76.26,56.87 77.1,56.89C78.04,56.95 78.04,56.95 79,57C79.33,57.66 79.66,58.32 80,59C76.7,59 73.4,59 70,59C70.48,63.65 70.48,63.65 72,68C70.68,67.67 69.36,67.34 68,67C67.75,60.38 67.75,60.38 70,57Z"
android:fillColor="#146A47"/>
<path
android:pathData="M188,19C189.32,19 190.64,19 192,19C192.33,19.99 192.66,20.98 193,22C192.01,22.66 191.02,23.32 190,24C190.66,25.65 191.32,27.3 192,29C191.01,29 190.02,29 189,29C187.19,26.81 187.19,26.81 186,24C186.81,21.19 186.81,21.19 188,19Z"
android:fillColor="#5DB87C"/>
<path
android:pathData="M127.06,16.94C128.52,16.97 128.52,16.97 130,17C130,18.32 130,19.64 130,21C129.34,21 128.68,21 128,21C128,21.66 128,22.32 128,23C126.68,23 125.36,23 124,23C123.62,21.34 123.29,19.68 123,18C124,17 124,17 127.06,16.94Z"
android:fillColor="#1F7354"/>
<path
android:pathData="M152,43C152.66,43 153.32,43 154,43C153.26,48.16 151.75,51.65 147.56,54.88C144.66,56.15 143.9,55.9 141,55C141.54,54.72 142.09,54.44 142.64,54.15C148.04,51.13 150.14,48.91 152,43Z"
android:fillColor="#157551"/>
<path
android:pathData="M49,53C49.66,53.33 50.32,53.66 51,54C52.37,54.07 53.75,54.08 55.13,54.06C57.04,54.03 57.04,54.03 59,54C59,54.66 59,55.32 59,56C57.42,56.03 55.83,56.05 54.25,56.06C52.93,56.08 52.93,56.08 51.58,56.1C49.21,56.01 47.29,55.57 45,55C42.99,55.27 40.99,55.59 39,56C41.21,51.59 44.68,51.75 49,53Z"
android:fillColor="#15744E"/>
<path
android:pathData="M205,30C205.66,30 206.32,30 207,30C207,30.66 207,31.32 207,32C207.66,32 208.32,32 209,32C213,36.47 213,36.47 213,39C213.99,39.33 214.98,39.66 216,40C215.01,41.49 215.01,41.49 214,43C212,42 212,42 211.19,39.75C209.74,36.39 207.6,34.53 205,32C205,31.34 205,30.68 205,30Z"
android:fillColor="#19734F"/>
<path
android:pathData="M33,55C33.66,55.33 34.32,55.66 35,56C33.63,58 33.63,58 32,60C31.34,60 30.68,60 30,60C29.67,60.99 29.34,61.98 29,63C25.44,64.19 25.44,64.19 22,65C22,64.34 22,63.68 22,63C22.89,62.61 23.77,62.22 24.69,61.81C28.12,59.93 30.37,57.87 33,55Z"
android:fillColor="#14744F"/>
<path
android:pathData="M131,6C131.99,6 132.98,6 134,6C134,8.64 134,11.28 134,14C132.35,14.33 130.7,14.66 129,15C129.35,14.4 129.7,13.8 130.06,13.19C131.33,10.89 131.33,10.89 130,8C130.33,7.34 130.66,6.68 131,6Z"
android:fillColor="#1C7451"/>
<path
android:pathData="M28.56,22.94C29.7,22.96 30.83,22.98 32,23C32.66,24.32 33.32,25.64 34,27C33.01,27.49 33.01,27.49 32,28C32,27.01 32,26.02 32,25C27.39,26.21 27.39,26.21 23,28C24.13,23.02 24.13,23.02 28.56,22.94Z"
android:fillColor="#16704D"/>
<path
android:pathData="M206,43C206,46.34 205.52,47.07 203.63,49.69C203.18,50.31 202.74,50.93 202.29,51.57C201.86,52.04 201.44,52.52 201,53C200.34,53 199.68,53 199,53C200.39,48.57 202.61,46.13 206,43Z"
android:fillColor="#5CB57B"/>
<path
android:pathData="M161,19C163.63,19.38 163.63,19.38 166,20C165.67,20.66 165.34,21.32 165,22C163.68,22 162.36,22 161,22C161,23.32 161,24.64 161,26C160.34,26 159.68,26 159,26C158.88,23.63 158.88,23.63 159,21C159.66,20.34 160.32,19.68 161,19Z"
android:fillColor="#5DB97B"/>
<path
android:pathData="M180,39C180.75,40.63 180.75,40.63 181,43C179.19,46.25 179.19,46.25 177,49C176.34,49 175.68,49 175,49C175.61,45.01 177.71,42.26 180,39Z"
android:fillColor="#5EBC7E"/>
<path
android:pathData="M176.56,18.94C177.77,18.97 177.77,18.97 179,19C179,19.66 179,20.32 179,21C177.68,21 176.36,21 175,21C175,22.65 175,24.3 175,26C174.34,26 173.68,26 173,26C172.96,24 172.96,22 173,20C174,19 174,19 176.56,18.94Z"
android:fillColor="#65BB7F"/>
<path
android:pathData="M150,12C152.48,12.49 152.48,12.49 155,13C155,13.99 155,14.98 155,16C153.35,15.67 151.7,15.34 150,15C150,14.01 150,13.02 150,12Z"
android:fillColor="#1E7855"/>
</vector>
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Some files were not shown because too many files have changed in this diff Show More