diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index b86273d..1b96c39 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,8 @@
-
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index de575c8..a2a5c8b 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -6,20 +6,29 @@
-
-
-
-
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -32,6 +41,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 0000000..01c7269
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,7 @@
+
+
+
+ Novin
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index eb50daa..0343747 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -13,6 +13,9 @@
+
+
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..f203762
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..c61ea33
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/planningMode.xml b/.idea/planningMode.xml
index 17c0abb..88fd199 100644
--- a/.idea/planningMode.xml
+++ b/.idea/planningMode.xml
@@ -4,6 +4,7 @@
diff --git a/build.gradle.kts b/build.gradle.kts
index 7763ad1..569a612 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -5,4 +5,5 @@ plugins {
alias(libs.plugins.kotlin.compose) apply false
id("com.google.dagger.hilt.android") version "2.59.2" apply false
alias(libs.plugins.ksp) apply false
+ alias(libs.plugins.jetbrains.kotlin.jvm) apply false
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 94a580c..9fa812f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -14,6 +14,7 @@ hilt = "2.59.2"
ksp = "2.3.6"
retrofit = "2.11.0"
okhttp = "4.12.0"
+jetbrainsKotlinJvm = "2.3.10"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -46,3 +47,4 @@ android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" }
diff --git a/p3/.gitignore b/p3/p3_pardakht_novin/.gitignore
similarity index 100%
rename from p3/.gitignore
rename to p3/p3_pardakht_novin/.gitignore
diff --git a/p3/build.gradle.kts b/p3/p3_pardakht_novin/build.gradle.kts
similarity index 96%
rename from p3/build.gradle.kts
rename to p3/p3_pardakht_novin/build.gradle.kts
index 5fabb75..b389dc2 100644
--- a/p3/build.gradle.kts
+++ b/p3/p3_pardakht_novin/build.gradle.kts
@@ -5,7 +5,7 @@ plugins {
}
android {
- namespace = "com.example.p3"
+ namespace = "com.example.p3_pardakht_novin"
compileSdk {
version = release(36) {
minorApiLevel = 1
diff --git a/p3/consumer-rules.pro b/p3/p3_pardakht_novin/consumer-rules.pro
similarity index 100%
rename from p3/consumer-rules.pro
rename to p3/p3_pardakht_novin/consumer-rules.pro
diff --git a/p3/libs/classes-2.1.17.jar b/p3/p3_pardakht_novin/libs/classes-2.1.17.jar
similarity index 100%
rename from p3/libs/classes-2.1.17.jar
rename to p3/p3_pardakht_novin/libs/classes-2.1.17.jar
diff --git a/p3/libs/platform_sdk_v2.3.706.jar b/p3/p3_pardakht_novin/libs/platform_sdk_v2.3.706.jar
similarity index 100%
rename from p3/libs/platform_sdk_v2.3.706.jar
rename to p3/p3_pardakht_novin/libs/platform_sdk_v2.3.706.jar
diff --git a/p3/proguard-rules.pro b/p3/p3_pardakht_novin/proguard-rules.pro
similarity index 100%
rename from p3/proguard-rules.pro
rename to p3/p3_pardakht_novin/proguard-rules.pro
diff --git a/p3/src/androidTest/java/com/example/p3/ExampleInstrumentedTest.kt b/p3/p3_pardakht_novin/src/androidTest/java/com/example/p3_pardakht_novin/ExampleInstrumentedTest.kt
similarity index 94%
rename from p3/src/androidTest/java/com/example/p3/ExampleInstrumentedTest.kt
rename to p3/p3_pardakht_novin/src/androidTest/java/com/example/p3_pardakht_novin/ExampleInstrumentedTest.kt
index c1f3f6a..05d8739 100644
--- a/p3/src/androidTest/java/com/example/p3/ExampleInstrumentedTest.kt
+++ b/p3/p3_pardakht_novin/src/androidTest/java/com/example/p3_pardakht_novin/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.example.p3
+package com.example.p3_pardakht_novin
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/p3/src/main/AndroidManifest.xml b/p3/p3_pardakht_novin/src/main/AndroidManifest.xml
similarity index 100%
rename from p3/src/main/AndroidManifest.xml
rename to p3/p3_pardakht_novin/src/main/AndroidManifest.xml
diff --git a/p3/src/main/java/com/example/p3/IapPosService.kt b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinIapPosService.kt
similarity index 95%
rename from p3/src/main/java/com/example/p3/IapPosService.kt
rename to p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinIapPosService.kt
index 583fd32..10aa8f6 100644
--- a/p3/src/main/java/com/example/p3/IapPosService.kt
+++ b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinIapPosService.kt
@@ -1,4 +1,4 @@
-package com.example.p3
+package com.example.p3_pardakht_novin
import android.content.Context
import android.content.Intent
@@ -6,15 +6,16 @@ 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.p3.models.IapAccountSplit
+import com.example.p3_pardakht_novin.models.IapAccountSplit
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
+import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class IapPosService @Inject constructor(
- private val context: Context
+class P3PardakhtNovinIapPosService @Inject constructor(
+ @param:ApplicationContext private val context: Context
) {
private val gson = Gson()
diff --git a/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinPrinter.kt b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinPrinter.kt
new file mode 100644
index 0000000..0e92070
--- /dev/null
+++ b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinPrinter.kt
@@ -0,0 +1,335 @@
+package com.example.p3_pardakht_novin
+
+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 P3PardakhtNovinPrinter @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 {
+ 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,
+ @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(TextPrintLine(" ", 0, 10))
+ addPrintLine(dividerLine)
+ addPrintLine(TextPrintLine(" ", 0, 10))
+
+ }
+ }
+
+
+ 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)
+ }
+}
diff --git a/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinPrinterHelper.kt b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinPrinterHelper.kt
new file mode 100644
index 0000000..c7be30c
--- /dev/null
+++ b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinPrinterHelper.kt
@@ -0,0 +1,173 @@
+package com.example.p3_pardakht_novin
+
+import android.device.PrinterManager
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.util.Log
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonElement
+import com.google.gson.JsonParser
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Helper class for interacting with the POS Printer.
+ */
+@Singleton
+class P3PardakhtNovinPrinterHelper @Inject constructor() {
+ private var printer: PrinterManager? = null
+ private val gson = GsonBuilder().setPrettyPrinting().create()
+
+ private val textPaint = Paint().apply {
+ textSize = 24f
+ color = Color.BLACK
+ isAntiAlias = true
+ }
+
+ init {
+ detectPrinter()
+ }
+
+ private fun detectPrinter() {
+ try {
+ // Attempt to instantiate the manager
+ val manager = PrinterManager()
+
+ try {
+ // Test the implementation.
+ // We use getStatus() to see if it's the system implementation or the stub.
+ val status = manager.getStatus()
+ printer = manager
+ Log.d("PrinterHelper", "PrinterManager initialized. Initial status: $status")
+ } catch (e: Exception) {
+ val errorMsg = e.cause?.message ?: e.message ?: ""
+ Log.w("PrinterHelper", "getStatus() failed with: $errorMsg")
+
+ if (errorMsg.contains("stub", ignoreCase = true)) {
+ Log.e("PrinterHelper", "CRITICAL: The SDK JAR is being bundled in your APK. You must Clean and Rebuild.")
+ } else {
+ // If it's not a 'stub' error, it might just be the hardware state
+ printer = manager
+ }
+ }
+ } catch (e: Throwable) {
+ Log.e("PrinterHelper", "PrinterManager not available on this device: ${e.message}")
+ }
+ }
+
+ fun getStatusMessage(code: Int): String {
+ return when (code) {
+ 0 -> "Printer is healthy"
+ -1 -> "No paper in printer"
+ 2 -> "Device is overheating"
+ -3 -> "Battery is too low to print"
+ else -> "Hardware error code: $code"
+ }
+ }
+
+ fun printJson(jsonData: String, onStatus: (String) -> Unit) {
+ try {
+ val jsonElement: JsonElement = JsonParser.parseString(jsonData)
+ val prettyJson = gson.toJson(jsonElement)
+ printLines(prettyJson.split("\n"), onStatus)
+ } catch (e: Exception) {
+ onStatus("Invalid JSON data provided.")
+ }
+ }
+
+ fun printLines(lines: List, onStatus: (String) -> Unit) {
+ if (printer == null) {
+ detectPrinter()
+ }
+
+ val currentPrinter = printer
+ if (currentPrinter == null) {
+ onStatus("Printer hardware not supported or SDK issue.")
+ return
+ }
+
+ Log.d("PrinterHelper", "Starting print job...")
+ var bitmap: Bitmap? = null
+ try {
+ currentPrinter.open()
+ currentPrinter.setupPage(384, -1)
+ currentPrinter.clearPage()
+
+ val lineHeight = 32
+ val totalHeight = (lines.size * lineHeight) + 40
+
+ bitmap = Bitmap.createBitmap(384, totalHeight, Bitmap.Config.ARGB_8888)
+ bitmap.eraseColor(Color.WHITE)
+ val canvas = Canvas(bitmap)
+
+ var yPos = 30f
+ for (line in lines) {
+ canvas.drawText(line, 10f, yPos, textPaint)
+ yPos += lineHeight
+ }
+
+ currentPrinter.drawBitmap(bitmap, 0, 0)
+ val printResult = currentPrinter.printPage(0)
+
+ if (printResult == 0) {
+ currentPrinter.paperFeed(20)
+ onStatus("Print Job Completed Successfully")
+ } else {
+ onStatus(getStatusMessage(printResult))
+ }
+
+ } catch (e: Exception) {
+ onStatus("Printer Error: ${e.localizedMessage}")
+ Log.e("PrinterHelper", "Unexpected error during printing", e)
+ } finally {
+ bitmap?.recycle()
+ try {
+ currentPrinter.close()
+ } catch (e: Throwable) {}
+ }
+ }
+
+/*
+ fun printText(): Int {
+ val manager = printer
+ val mCenterPaint = Paint().apply {
+ textSize = 30f
+ color = Color.BLACK
+ }
+ var mBitmap: Bitmap? = null
+ manager.setupPage(384, -1)
+ manager.clearPage()
+
+ val mconfig = Bitmap.Config.ARGB_8888
+ mBitmap = Bitmap.createBitmap(384, 2000, mconfig)
+ mBitmap.eraseColor(-0x1) // 0xffffffff
+
+ val mCanvas = Canvas(mBitmap)
+
+ mCanvas.drawText("سلام", 330f, 30f, mCenterPaint)
+ mCanvas.drawText("حال شما خوب هست", 165f, 60f, mCenterPaint)
+ mCanvas.drawText("سلام", 330f, 90f, mCenterPaint)
+ mCanvas.drawText("حال شما خوب هست", 165f, 120f, mCenterPaint)
+
+ var mirrored: Bitmap? = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.width, 130)
+ manager.drawBitmap(mirrored, 0, 0)
+
+ val ret = manager.printPage(0)
+ manager.paperFeed(15)
+
+ mBitmap?.let {
+ it.recycle()
+ mBitmap = null
+ }
+
+ mirrored?.let {
+ it.recycle()
+ mirrored = null
+ }
+
+ return ret
+ }
+*/
+}
diff --git a/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinService.kt b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinService.kt
new file mode 100644
index 0000000..c12c711
--- /dev/null
+++ b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/P3PardakhtNovinService.kt
@@ -0,0 +1,47 @@
+package com.example.p3_pardakht_novin
+
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import com.example.core.domain.PspService
+import com.example.core.model.PaymentResultEntity
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import javax.inject.Inject
+
+class P3PardakhtNovinService @Inject constructor(
+ private val p3PardakhtNovinIapPosService: P3PardakhtNovinIapPosService,
+ private val p3PardakhtNovinPrinterHelper: P3PardakhtNovinPrinterHelper
+) : PspService {
+
+ private val scope = CoroutineScope(Dispatchers.IO)
+
+ override fun pay(amount: Long): Intent {
+ return p3PardakhtNovinIapPosService.startPurchase(amount)
+
+ }
+
+ override fun print(lines: List, onMessage: (String) -> Unit) {
+ p3PardakhtNovinPrinterHelper.printLines(lines) { status ->
+ onMessage("P3 Printer: $status")
+ }
+ }
+
+
+ override fun decodePosResponse(
+ id: String?,
+ activityResult: ActivityResult
+ ): PaymentResultEntity {
+ return try {
+ p3PardakhtNovinIapPosService.decodeResponse(
+ id = id,
+ activityResult = activityResult
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ PaymentResultEntity.failure(
+ id = id,
+ e.message
+ )
+ }
+ }
+}
diff --git a/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/di/P3PardakhtNovinModule.kt b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/di/P3PardakhtNovinModule.kt
new file mode 100644
index 0000000..8b8dc0d
--- /dev/null
+++ b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/di/P3PardakhtNovinModule.kt
@@ -0,0 +1,11 @@
+package com.example.p3_pardakht_novin.di
+
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+object P3PardakhtNovinModule {
+ // P3PardakhtNovinIapPosService is provided via @Inject constructor
+}
diff --git a/p3/src/main/java/com/example/p3/models/IapAccountSplit.kt b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/models/IapAccountSplit.kt
similarity index 61%
rename from p3/src/main/java/com/example/p3/models/IapAccountSplit.kt
rename to p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/models/IapAccountSplit.kt
index 7e35015..abea801 100644
--- a/p3/src/main/java/com/example/p3/models/IapAccountSplit.kt
+++ b/p3/p3_pardakht_novin/src/main/java/com/example/p3_pardakht_novin/models/IapAccountSplit.kt
@@ -1,4 +1,4 @@
-package com.example.p3.models
+package com.example.p3_pardakht_novin.models
data class IapAccountSplit(
val index: Int,
diff --git a/p3/src/main/res/font/font.ttf b/p3/p3_pardakht_novin/src/main/res/font/font.ttf
similarity index 100%
rename from p3/src/main/res/font/font.ttf
rename to p3/p3_pardakht_novin/src/main/res/font/font.ttf
diff --git a/p3/src/test/java/com/example/p3/ExampleUnitTest.kt b/p3/p3_pardakht_novin/src/test/java/com/example/p3_pardakht_novin/ExampleUnitTest.kt
similarity index 89%
rename from p3/src/test/java/com/example/p3/ExampleUnitTest.kt
rename to p3/p3_pardakht_novin/src/test/java/com/example/p3_pardakht_novin/ExampleUnitTest.kt
index 84c5d00..a4ae248 100644
--- a/p3/src/test/java/com/example/p3/ExampleUnitTest.kt
+++ b/p3/p3_pardakht_novin/src/test/java/com/example/p3_pardakht_novin/ExampleUnitTest.kt
@@ -1,4 +1,4 @@
-package com.example.p3
+package com.example.p3_pardakht_novin
import org.junit.Test
diff --git a/p3/p3_sepehr/.gitignore b/p3/p3_sepehr/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/p3/p3_sepehr/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/p3/p3_sepehr/build.gradle.kts b/p3/p3_sepehr/build.gradle.kts
new file mode 100644
index 0000000..aa93200
--- /dev/null
+++ b/p3/p3_sepehr/build.gradle.kts
@@ -0,0 +1,49 @@
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.hilt)
+}
+
+android {
+ namespace = "com.example.p3_sepehr"
+ compileSdk {
+ version = release(36) {
+ minorApiLevel = 1
+ }
+ }
+
+ defaultConfig {
+ minSdk = 24
+ 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 {
+ implementation(project(":core"))
+ implementation(libs.hilt.android)
+ ksp(libs.hilt.compiler)
+
+ implementation(libs.retrofit.converter.gson)
+
+ implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.material)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.junit)
+}
diff --git a/p3/p3_sepehr/consumer-rules.pro b/p3/p3_sepehr/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/p3/p3_sepehr/libs/classes-2.1.17.jar b/p3/p3_sepehr/libs/classes-2.1.17.jar
new file mode 100644
index 0000000..352ddc8
Binary files /dev/null and b/p3/p3_sepehr/libs/classes-2.1.17.jar differ
diff --git a/p3/p3_sepehr/libs/platform_sdk_v2.3.706.jar b/p3/p3_sepehr/libs/platform_sdk_v2.3.706.jar
new file mode 100644
index 0000000..f9068b3
Binary files /dev/null and b/p3/p3_sepehr/libs/platform_sdk_v2.3.706.jar differ
diff --git a/p3/p3_sepehr/proguard-rules.pro b/p3/p3_sepehr/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/p3/p3_sepehr/proguard-rules.pro
@@ -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
\ No newline at end of file
diff --git a/p3/p3_sepehr/src/androidTest/java/com/example/p3_pardakht_novin/ExampleInstrumentedTest.kt b/p3/p3_sepehr/src/androidTest/java/com/example/p3_pardakht_novin/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..05d8739
--- /dev/null
+++ b/p3/p3_sepehr/src/androidTest/java/com/example/p3_pardakht_novin/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.p3_pardakht_novin
+
+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.ps3.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/p3/p3_sepehr/src/main/AndroidManifest.xml b/p3/p3_sepehr/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a255a02
--- /dev/null
+++ b/p3/p3_sepehr/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrIapPosService.kt b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrIapPosService.kt
new file mode 100644
index 0000000..9c97738
--- /dev/null
+++ b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrIapPosService.kt
@@ -0,0 +1,78 @@
+package com.example.p3_pardakht_novin
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import androidx.activity.result.ActivityResult
+import com.example.core.model.PaymentResultEntity
+import com.example.core.model.PaymentResultStatus
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class P3SepehrIapPosService @Inject constructor(
+ @param:ApplicationContext private val context: Context
+) {
+
+ fun isPosAppInstalled(packageName: String = "com.dml.sima7.sepehr"): Boolean {
+ return try {
+ context.packageManager.getPackageInfo(packageName, 0)
+ true
+ } catch (e: PackageManager.NameNotFoundException) {
+ false
+ }
+ }
+
+ fun startPurchase(
+ amount: Long,
+ packageName: String = "com.dml.sima7.sepehr"
+ ): Intent {
+ return Intent("com.dml.sima7.sepehr.activity.Intent_SwipeCardActivity").apply {
+ setPackage(packageName)
+ putExtra("amount", amount.toString())
+ setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
+ }
+
+ fun decodeResponse(
+ id: String?,
+ activityResult: ActivityResult
+ ): PaymentResultEntity {
+ val data = activityResult.data
+
+ if (data == null) {
+ return PaymentResultEntity.failure(
+ id = id,
+ message = "پاسخی از دستگاه دریافت نشد!"
+ )
+ }
+
+ val resultCode = data.getStringExtra("resultCode")
+ val resultDescription = data.getStringExtra("resultDescription") ?: "خطای نامشخص"
+
+ // resultCode "00" usually means success in most Iranian PSPs
+ val isSuccess = resultCode == "00"
+
+ return if (isSuccess) {
+ PaymentResultEntity(
+ id = id,
+ status = PaymentResultStatus.SUCCESS,
+ paymentResultDataEntity = PaymentResultEntity.PaymentResultDataEntity(
+ terminalId = data.getStringExtra("terminalNo") ?: "",
+ stan = data.getStringExtra("traceNo") ?: "",
+ rrn = data.getStringExtra("refrenceCode") ?: "",
+ responseCode = resultCode,
+ customerCardNO = data.getStringExtra("maskedPan") ?: "",
+ transactionDateTime = data.getStringExtra("DateTime") ?: "",
+ ),
+ message = resultDescription
+ )
+ } else {
+ PaymentResultEntity.failure(
+ id = id,
+ message = resultDescription
+ )
+ }
+ }
+}
diff --git a/p3/src/main/java/com/example/p3/P3Printer.kt b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrPrinter.kt
similarity index 99%
rename from p3/src/main/java/com/example/p3/P3Printer.kt
rename to p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrPrinter.kt
index 7384401..330b930 100644
--- a/p3/src/main/java/com/example/p3/P3Printer.kt
+++ b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrPrinter.kt
@@ -1,4 +1,4 @@
-package com.example.p3
+package com.example.p3_pardakht_novin
import android.content.Context
import android.graphics.Bitmap
@@ -20,7 +20,7 @@ import java.io.File
import javax.inject.Inject
-class P3Printer @Inject constructor(
+class P3SepehrPrinter @Inject constructor(
@param:ApplicationContext private val context: Context,
) : Printer {
companion object {
diff --git a/p3/src/main/java/com/example/p3/PrinterHelper.kt b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrPrinterHelper.kt
similarity index 98%
rename from p3/src/main/java/com/example/p3/PrinterHelper.kt
rename to p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrPrinterHelper.kt
index 95331ec..cc51317 100644
--- a/p3/src/main/java/com/example/p3/PrinterHelper.kt
+++ b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrPrinterHelper.kt
@@ -1,4 +1,4 @@
-package com.example.p3
+package com.example.p3_pardakht_novin
import android.device.PrinterManager
import android.graphics.Bitmap
@@ -16,7 +16,7 @@ import javax.inject.Singleton
* Helper class for interacting with the POS Printer.
*/
@Singleton
-class PrinterHelper @Inject constructor() {
+class P3SepehrPrinterHelper @Inject constructor() {
private var printer: PrinterManager? = null
private val gson = GsonBuilder().setPrettyPrinting().create()
diff --git a/p3/src/main/java/com/example/p3/P3Service.kt b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrService.kt
similarity index 72%
rename from p3/src/main/java/com/example/p3/P3Service.kt
rename to p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrService.kt
index f455010..3d63363 100644
--- a/p3/src/main/java/com/example/p3/P3Service.kt
+++ b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/P3SepehrService.kt
@@ -1,4 +1,4 @@
-package com.example.p3
+package com.example.p3_pardakht_novin
import android.content.Intent
import androidx.activity.result.ActivityResult
@@ -8,20 +8,20 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
-class P3Service @Inject constructor(
- private val iapPosService: IapPosService,
- private val printerHelper: PrinterHelper
+class P3SepehrService @Inject constructor(
+ private val p3SepehrIapPosService: P3SepehrIapPosService,
+ private val p3SepehrPrinterHelper: P3SepehrPrinterHelper
) : PspService {
private val scope = CoroutineScope(Dispatchers.IO)
override fun pay(amount: Long): Intent {
- return iapPosService.startPurchase(amount)
+ return p3SepehrIapPosService.startPurchase(amount)
}
override fun print(lines: List, onMessage: (String) -> Unit) {
- printerHelper.printLines(lines) { status ->
+ p3SepehrPrinterHelper.printLines(lines) { status ->
onMessage("P3 Printer: $status")
}
}
@@ -32,7 +32,7 @@ class P3Service @Inject constructor(
activityResult: ActivityResult
): PaymentResultEntity {
return try {
- iapPosService.decodeResponse(
+ p3SepehrIapPosService.decodeResponse(
id = id,
activityResult = activityResult
)
diff --git a/p3/src/main/java/com/example/p3/di/P3Module.kt b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/di/P3SepehrModule.kt
similarity index 66%
rename from p3/src/main/java/com/example/p3/di/P3Module.kt
rename to p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/di/P3SepehrModule.kt
index 3804667..7e377e8 100644
--- a/p3/src/main/java/com/example/p3/di/P3Module.kt
+++ b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/di/P3SepehrModule.kt
@@ -1,7 +1,7 @@
-package com.example.p3.di
+package com.example.p3_pardakht_novin.di
import android.content.Context
-import com.example.p3.IapPosService
+import com.example.p3_pardakht_novin.P3SepehrIapPosService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -11,11 +11,11 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
-object P3Module {
+object P3SepehrModule {
@Provides
@Singleton
- fun provideIapPosService(@ApplicationContext context: Context): IapPosService {
- return IapPosService(context)
+ fun provideIapPosService(@ApplicationContext context: Context): P3SepehrIapPosService {
+ return P3SepehrIapPosService(context)
}
}
diff --git a/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/models/IapAccountSplit.kt b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/models/IapAccountSplit.kt
new file mode 100644
index 0000000..abea801
--- /dev/null
+++ b/p3/p3_sepehr/src/main/java/com/example/p3_pardakht_novin/models/IapAccountSplit.kt
@@ -0,0 +1,6 @@
+package com.example.p3_pardakht_novin.models
+
+data class IapAccountSplit(
+ val index: Int,
+ val percent: Int
+)
diff --git a/p3/p3_sepehr/src/main/res/font/font.ttf b/p3/p3_sepehr/src/main/res/font/font.ttf
new file mode 100644
index 0000000..cc9d9b4
Binary files /dev/null and b/p3/p3_sepehr/src/main/res/font/font.ttf differ
diff --git a/p3/p3_sepehr/src/test/java/com/example/p3_pardakht_novin/ExampleUnitTest.kt b/p3/p3_sepehr/src/test/java/com/example/p3_pardakht_novin/ExampleUnitTest.kt
new file mode 100644
index 0000000..a4ae248
--- /dev/null
+++ b/p3/p3_sepehr/src/test/java/com/example/p3_pardakht_novin/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.p3_pardakht_novin
+
+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)
+ }
+}
\ No newline at end of file
diff --git a/pardakht_novin_app/.gitignore b/pardakht_novin_app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/pardakht_novin_app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/pardakht_novin_app/build.gradle.kts b/pardakht_novin_app/build.gradle.kts
new file mode 100644
index 0000000..c3cea58
--- /dev/null
+++ b/pardakht_novin_app/build.gradle.kts
@@ -0,0 +1,92 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.hilt)
+}
+
+android {
+ namespace = "com.example.pardakht_novin_app"
+ compileSdk {
+ version = release(36) {
+ minorApiLevel = 1
+ }
+ }
+
+ defaultConfig {
+ applicationId = "com.example.pardakht_novin_app"
+ minSdk = 24
+ targetSdk = 36
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ flavorDimensions.add("module")
+ productFlavors {
+ create("p3_pardakht_novin") {
+ versionCode = 1
+ versionName = "1.0"
+ dimension = "module"
+ applicationId = "com.example.pardakht_novin_app_p3_pardakht_novin"
+ manifestPlaceholders["appName"] = "پرداخت نوین"
+ }
+ create("p3_sepehr") {
+ versionCode = 1
+ versionName = "1.0"
+ dimension = "module"
+ applicationId = "com.example.pardakht_novin_app_p3_sepehr"
+ manifestPlaceholders["appName"] = "تیس (سپهر)"
+ }
+ }
+
+ buildTypes {
+ debug {
+ applicationIdSuffix = ".debug"
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ buildFeatures {
+ compose = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ implementation(project(":core"))
+ implementation(project(":design_system"))
+
+ "p3_pardakht_novinImplementation"(project(":p3:p3_pardakht_novin"))
+ "p3_sepehrImplementation"(project(":p3:p3_sepehr"))
+
+ 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.retrofit.converter.gson)
+ 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)
+}
\ No newline at end of file
diff --git a/pardakht_novin_app/proguard-rules.pro b/pardakht_novin_app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/pardakht_novin_app/proguard-rules.pro
@@ -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
\ No newline at end of file
diff --git a/pardakht_novin_app/src/androidTest/java/com/example/pardakht_novin_app/ExampleInstrumentedTest.kt b/pardakht_novin_app/src/androidTest/java/com/example/pardakht_novin_app/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..303076b
--- /dev/null
+++ b/pardakht_novin_app/src/androidTest/java/com/example/pardakht_novin_app/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.example.pardakht_novin_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.pardakht_novin_app", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/AndroidManifest.xml b/pardakht_novin_app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1944676
--- /dev/null
+++ b/pardakht_novin_app/src/main/AndroidManifest.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/ic_launcher-playstore.png b/pardakht_novin_app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..b1d48e8
Binary files /dev/null and b/pardakht_novin_app/src/main/ic_launcher-playstore.png differ
diff --git a/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/PardakhtNovinMainActivity.kt b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/PardakhtNovinMainActivity.kt
new file mode 100644
index 0000000..1e753aa
--- /dev/null
+++ b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/PardakhtNovinMainActivity.kt
@@ -0,0 +1,69 @@
+package com.example.pardakht_novin_app.ui
+
+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.pardakht_novin_app.ui.screen.PardakhtNovinWebViewScreen
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class PardakhtNovinMainActivity : 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 ->
+
+
+ PardakhtNovinWebViewScreen(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/PspApplication.kt b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/PspApplication.kt
new file mode 100644
index 0000000..aa88f54
--- /dev/null
+++ b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/PspApplication.kt
@@ -0,0 +1,13 @@
+package com.example.pardakht_novin_app.ui
+
+import android.app.Application
+import com.example.design_system.util.WebViewManager
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class PspApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ WebViewManager.preload(this)
+ }
+}
diff --git a/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/screen/PardakhtNovinWebViewScreen.kt b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/screen/PardakhtNovinWebViewScreen.kt
new file mode 100644
index 0000000..bebf7ae
--- /dev/null
+++ b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/screen/PardakhtNovinWebViewScreen.kt
@@ -0,0 +1,101 @@
+package com.example.pardakht_novin_app.ui.screen
+
+import android.util.Log
+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.rememberCoroutineScope
+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.core.getAndroidId
+import com.example.core.getDeviceName
+import com.example.design_system.PspWebView
+import com.example.pardakht_novin_app.ui.viewmodel.WebViewModel
+import kotlinx.coroutines.launch
+
+private const val tisWebUrl = "https://tis.shift-am.ir"
+private const val tisWebViewTag = "TisWebViewScreen"
+
+@Composable
+fun PardakhtNovinWebViewScreen(
+ modifier: Modifier = Modifier,
+ viewModel: WebViewModel = hiltViewModel()
+) {
+
+ val context = LocalContext.current
+ var webView: WebView? by remember { mutableStateOf(null) }
+ var currentPaymentId by rememberSaveable { mutableStateOf(null) }
+ var isPaymentProcessing by rememberSaveable { mutableStateOf(false) }
+ var isPrinting by rememberSaveable { mutableStateOf(false) }
+ var initialCallbackSent by rememberSaveable { mutableStateOf(false) }
+ val coroutineScope = rememberCoroutineScope()
+ val deviceInfo by remember {
+ mutableStateOf(
+ viewModel.deviceInfoJson(
+ androidId = getAndroidId(context),
+ getDeviceName()
+ )
+ )
+ }
+
+ // launcher to handle POS app result
+ val posLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.StartActivityForResult()
+ ) { result ->
+ val paymentResultEntity = viewModel.paymentResult(
+ id = currentPaymentId,
+ activityResult = result
+ )
+ val jsResult = viewModel.postPaymentResult(paymentResultEntity)
+ webView?.let {
+ it.post {
+ it.evaluateJavascript(jsResult, null)
+ }
+ } ?: run {
+ Log.w(tisWebViewTag, "Skipping onPaymentResult JS callback: WebView is not ready yet.")
+ }
+ isPaymentProcessing = false
+ }
+
+ PspWebView(
+ url = tisWebUrl,
+ modifier = modifier,
+ onPayClick = { amount: Long, id: String? ->
+ if (isPaymentProcessing) return@PspWebView
+ isPaymentProcessing = true
+ currentPaymentId = id
+ viewModel.pay(amount)
+ .also(posLauncher::launch)
+ },
+ onPrintClick = { printEntities ->
+ if (isPrinting) return@PspWebView
+ isPrinting = true
+ coroutineScope.launch {
+ try {
+ viewModel.print(printEntities)
+ } finally {
+ isPrinting = false
+ }
+ }
+ },
+ onDeviceInfo = {
+ deviceInfo
+ },
+ onWebViewReady = { readyWebView ->
+ webView = readyWebView
+ },
+ onPageFinished = { loadedWebView ->
+ if (!initialCallbackSent) {
+ loadedWebView.evaluateJavascript(viewModel.postPaymentResult(null), null)
+ initialCallbackSent = true
+ }
+ }
+ )
+}
diff --git a/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/viewmodel/WebViewModel.kt b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/viewmodel/WebViewModel.kt
new file mode 100644
index 0000000..3812b19
--- /dev/null
+++ b/pardakht_novin_app/src/main/java/com/example/pardakht_novin_app/ui/viewmodel/WebViewModel.kt
@@ -0,0 +1,57 @@
+package com.example.pardakht_novin_app.ui.viewmodel
+
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import androidx.lifecycle.ViewModel
+import com.example.core.domain.PaymentResult
+import com.example.core.domain.Printer
+import com.example.core.domain.PspService
+import com.example.core.model.DeviceInfo
+import com.example.core.model.PaymentResultEntity
+import com.example.core.model.PrintEntity
+import com.example.pardakht_novin_app.R
+import com.google.gson.Gson
+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 printer: Printer,
+ private val gson: Gson
+) : 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
+ )
+ }
+
+ fun print(printEntities: List) {
+ printer.print(
+ printEntities = printEntities,
+ icon = R.drawable.ic_pardakht_novin
+ )
+ }
+
+ fun deviceInfoJson(androidId: String, deviceName: String): String {
+ return gson.toJson(
+ DeviceInfo(
+ androidId = androidId,
+ deviceName = deviceName
+ )
+ )
+ }
+}
diff --git a/pardakht_novin_app/src/main/res/drawable/ic_launcher_background.xml b/pardakht_novin_app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pardakht_novin_app/src/main/res/drawable/ic_launcher_foreground.xml b/pardakht_novin_app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/res/drawable/ic_pardakht_novin.xml b/pardakht_novin_app/src/main/res/drawable/ic_pardakht_novin.xml
new file mode 100644
index 0000000..a6607c4
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/drawable/ic_pardakht_novin.xml
@@ -0,0 +1,381 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pardakht_novin_app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/pardakht_novin_app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..65291b9
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/pardakht_novin_app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..65291b9
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher.webp b/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..0ebbbd4
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..f92ec0c
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..598c01f
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher.webp b/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..2f012e7
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..beb2947
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..8c9e3bd
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..5e904a0
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..805ff39
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..042996a
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..7acadef
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..fc1e93a
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..f47edc0
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..75d531c
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..2444704
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..629dcd5
Binary files /dev/null and b/pardakht_novin_app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/pardakht_novin_app/src/main/res/values/colors.xml b/pardakht_novin_app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/res/values/ic_launcher_background.xml b/pardakht_novin_app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..c5d5899
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/res/values/strings.xml b/pardakht_novin_app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a4e3977
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ pardakht_novin_app
+
\ No newline at end of file
diff --git a/pardakht_novin_app/src/main/res/values/themes.xml b/pardakht_novin_app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..7488cfe
--- /dev/null
+++ b/pardakht_novin_app/src/main/res/values/themes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/tis_app/src/p3/java/com/example/tis_app/di/PspModule.kt b/pardakht_novin_app/src/p3_pardakht_novin/java/com/example/tis_app/di/PspModule.kt
similarity index 55%
rename from tis_app/src/p3/java/com/example/tis_app/di/PspModule.kt
rename to pardakht_novin_app/src/p3_pardakht_novin/java/com/example/tis_app/di/PspModule.kt
index 8d208f7..6a26155 100644
--- a/tis_app/src/p3/java/com/example/tis_app/di/PspModule.kt
+++ b/pardakht_novin_app/src/p3_pardakht_novin/java/com/example/tis_app/di/PspModule.kt
@@ -2,8 +2,8 @@ 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 com.example.p3_pardakht_novin.P3PardakhtNovinPrinter
+import com.example.p3_pardakht_novin.P3PardakhtNovinService
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@@ -13,8 +13,8 @@ import dagger.hilt.components.SingletonComponent
@InstallIn(SingletonComponent::class)
abstract class PspModule {
@Binds
- abstract fun bindPspService(impl: P3Service): PspService
+ abstract fun bindPspService(impl: P3PardakhtNovinService): PspService
@Binds
- abstract fun bindPrinter(impl: P3Printer): Printer
+ abstract fun bindPrinter(impl: P3PardakhtNovinPrinter): Printer
}
diff --git a/pardakht_novin_app/src/ps4/java/com/example/tis_sep/di/PspModule.kt b/pardakht_novin_app/src/ps4/java/com/example/tis_sep/di/PspModule.kt
new file mode 100644
index 0000000..eece775
--- /dev/null
+++ b/pardakht_novin_app/src/ps4/java/com/example/tis_sep/di/PspModule.kt
@@ -0,0 +1,20 @@
+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
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class PspModule {
+ @Binds
+ abstract fun bindPspService(impl: Ps4Service): PspService
+
+ @Binds
+ abstract fun bindPrinter(impl: P4Printer): Printer
+
+}
diff --git a/pardakht_novin_app/src/standard/java/com/example/tis_sep/di/PspModule.kt b/pardakht_novin_app/src/standard/java/com/example/tis_sep/di/PspModule.kt
new file mode 100644
index 0000000..96ef61d
--- /dev/null
+++ b/pardakht_novin_app/src/standard/java/com/example/tis_sep/di/PspModule.kt
@@ -0,0 +1,24 @@
+package com.example.tis_sep.di
+
+import android.content.Intent
+import com.example.core.PspService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+object PspModule {
+ @Provides
+ fun providePspService(): PspService = object : PspService {
+ override fun pay(amount: Double, onMessage: (String) -> Unit) {
+ onMessage("Standard: Payment not supported")
+ }
+ override fun print(text: String, onMessage: (String) -> Unit) {
+ onMessage("Standard: Printing not supported")
+ }
+ override fun createPurchaseIntent(amount: Long): Intent? = null
+ override fun decodePosResponse(response: String?): Map? = null
+ }
+}
diff --git a/pardakht_novin_app/src/test/java/com/example/pardakht_novin_app/ExampleUnitTest.kt b/pardakht_novin_app/src/test/java/com/example/pardakht_novin_app/ExampleUnitTest.kt
new file mode 100644
index 0000000..14661f8
--- /dev/null
+++ b/pardakht_novin_app/src/test/java/com/example/pardakht_novin_app/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.pardakht_novin_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)
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 0a83031..ef49f3c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -25,8 +25,13 @@ dependencyResolutionManagement {
rootProject.name = "PSP"
include(":app")
include(":tis_app")
-include(":p3")
include(":ps4")
include(":core")
include(":design_system")
include(":stage_app")
+
+// P3 Parent and children
+include(":p3")
+include(":p3:p3_pardakht_novin")
+include(":p3:p3_sepehr")
+include(":pardakht_novin_app")
diff --git a/stage_app/build.gradle.kts b/stage_app/build.gradle.kts
index 702c219..96b108c 100644
--- a/stage_app/build.gradle.kts
+++ b/stage_app/build.gradle.kts
@@ -23,13 +23,19 @@ android {
flavorDimensions.add("module")
productFlavors {
- create("p3") {
+ create("p3_novin") {
versionCode = 1
versionName = "1.0"
dimension = "module"
- applicationId = "com.example.stage_app_p3"
- manifestPlaceholders["appName"] = "TisP3"
-
+ applicationId = "com.example.stage_app_p3_novin"
+ manifestPlaceholders["appName"] = "TisP3Novin"
+ }
+ create("p3_sepehr") {
+ versionCode = 1
+ versionName = "1.0"
+ dimension = "module"
+ applicationId = "com.example.stage_app_p3_sepehr"
+ manifestPlaceholders["appName"] = "TisP3Sepehr"
}
create("ps4") {
versionCode = 1
@@ -44,11 +50,14 @@ android {
dimension = "module"
applicationId = "com.example.stage_app"
manifestPlaceholders["appName"] = "Tis"
-
}
}
buildTypes {
+ debug {
+ applicationIdSuffix = ".debug"
+ signingConfig = signingConfigs.getByName("debug")
+ }
release {
isMinifyEnabled = false
proguardFiles(
@@ -67,14 +76,14 @@ android {
compose = true
buildConfig = true
}
-
}
dependencies {
implementation(project(":core"))
implementation(project(":design_system"))
- "p3Implementation"(project(":p3"))
-
+
+ "p3_novinImplementation"(project(":p3:p3_pardakht_novin"))
+ "p3_sepehrImplementation"(project(":p3:p3_sepehr"))
"ps4Implementation"(project(":ps4"))
implementation(libs.hilt.android)
@@ -97,4 +106,4 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)
-}
\ No newline at end of file
+}
diff --git a/stage_app/src/p3/java/com/example/tis_app/di/PspModule.kt b/stage_app/src/p3/java/com/example/tis_app/di/PspModule.kt
index 8d208f7..fde711f 100644
--- a/stage_app/src/p3/java/com/example/tis_app/di/PspModule.kt
+++ b/stage_app/src/p3/java/com/example/tis_app/di/PspModule.kt
@@ -2,8 +2,8 @@ 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 com.example.p3_pardakht_novin.P3Printer
+import com.example.p3_pardakht_novin.P3Service
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
diff --git a/tis_app/build.gradle.kts b/tis_app/build.gradle.kts
index 4306bdd..04607d6 100644
--- a/tis_app/build.gradle.kts
+++ b/tis_app/build.gradle.kts
@@ -22,28 +22,27 @@ android {
flavorDimensions.add("module")
productFlavors {
- create("p3") {
+ create("p3_pardakht_novin") {
versionCode = 1
versionName = "1.0"
dimension = "module"
- applicationId = "com.example.tis_app_p3"
- manifestPlaceholders["appName"] = "تیس"
+ applicationId = "com.example.tis_app_p3_pardakht_novin"
+ manifestPlaceholders["appName"] = "تیس (نوین)"
}
- create("ps4") {
+ create("p3_sepehr") {
versionCode = 1
versionName = "1.0"
dimension = "module"
- applicationId = "com.example.tis_app_p4"
- manifestPlaceholders["appName"] = "تیس" }
- create("standard") {
- versionCode = 1
- versionName = "1.0"
- dimension = "module"
- applicationId = "com.example.tis_app"
- manifestPlaceholders["appName"] = "تیس" }
+ applicationId = "com.example.tis_app_p3_sepehr"
+ manifestPlaceholders["appName"] = "تیس (سپهر)"
+ }
}
buildTypes {
+ debug {
+ applicationIdSuffix = ".debug"
+ signingConfig = signingConfigs.getByName("debug")
+ }
release {
isMinifyEnabled = false
proguardFiles(
@@ -66,10 +65,10 @@ android {
dependencies {
implementation(project(":core"))
implementation(project(":design_system"))
- "p3Implementation"(project(":p3"))
-
- "ps4Implementation"(project(":ps4"))
+ "p3_pardakht_novinImplementation"(project(":p3:p3_pardakht_novin"))
+ "p3_sepehrImplementation"(project(":p3:p3_sepehr"))
+
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
diff --git a/tis_app/src/main/java/com/example/tis_app/screen/TisWebViewScreen.kt b/tis_app/src/main/java/com/example/tis_app/screen/TisWebViewScreen.kt
index 78511b6..47b4a22 100644
--- a/tis_app/src/main/java/com/example/tis_app/screen/TisWebViewScreen.kt
+++ b/tis_app/src/main/java/com/example/tis_app/screen/TisWebViewScreen.kt
@@ -20,7 +20,7 @@ import com.example.core.getDeviceName
import com.example.design_system.PspWebView
import com.example.tis_app.viewmodel.WebViewModel
-private const val tisWebUrl = "https://tis.shift-am.ir"
+private const val tisWebUrl = "192.168.128.73:5000"
private const val tisWebViewTag = "TisWebViewScreen"
@Composable
diff --git a/tis_app/src/p3_pardakht_novin/java/com/example/tis_app/di/PspModule.kt b/tis_app/src/p3_pardakht_novin/java/com/example/tis_app/di/PspModule.kt
new file mode 100644
index 0000000..6a26155
--- /dev/null
+++ b/tis_app/src/p3_pardakht_novin/java/com/example/tis_app/di/PspModule.kt
@@ -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_pardakht_novin.P3PardakhtNovinPrinter
+import com.example.p3_pardakht_novin.P3PardakhtNovinService
+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: P3PardakhtNovinService): PspService
+
+ @Binds
+ abstract fun bindPrinter(impl: P3PardakhtNovinPrinter): Printer
+}