Dola TENG 2 gadi atpakaļ
revīzija
9260ca5143
100 mainītis faili ar 8250 papildinājumiem un 0 dzēšanām
  1. 16 0
      .gitignore
  2. 3 0
      .idea/.gitignore
  3. 1 0
      .idea/.name
  4. 123 0
      .idea/codeStyles/Project.xml
  5. 5 0
      .idea/codeStyles/codeStyleConfig.xml
  6. 6 0
      .idea/compiler.xml
  7. 21 0
      .idea/gradle.xml
  8. 35 0
      .idea/jarRepositories.xml
  9. 101 0
      .idea/misc.xml
  10. 6 0
      .idea/vcs.xml
  11. 1 0
      app/.gitignore
  12. 161 0
      app/build.gradle
  13. 39 0
      app/google-services.json
  14. 18 0
      app/prod/debug/output-metadata.json
  15. 116 0
      app/proguard-rules.pro
  16. 24 0
      app/src/androidTest/java/com/khmer9/yuki/ExampleInstrumentedTest.kt
  17. 6 0
      app/src/dev/java/com/khmer9/yuki/config/Config.kt
  18. 48 0
      app/src/main/AndroidManifest.xml
  19. BIN
      app/src/main/ic_launcher-playstore.png
  20. BIN
      app/src/main/ic_logo-playstore.png
  21. 47 0
      app/src/main/java/com/khmer9/yuki/adapter/AbstractAdapter.kt
  22. 23 0
      app/src/main/java/com/khmer9/yuki/adapter/AbstractViewHolder.kt
  23. 29 0
      app/src/main/java/com/khmer9/yuki/adapter/BaseListAdapter.kt
  24. 21 0
      app/src/main/java/com/khmer9/yuki/adapter/BaseListItem.kt
  25. 44 0
      app/src/main/java/com/khmer9/yuki/adapter/BaseListViewHolder.kt
  26. 23 0
      app/src/main/java/com/khmer9/yuki/adapter/BettingStatementAdapter.kt
  27. 121 0
      app/src/main/java/com/khmer9/yuki/adapter/BettingStatementHeaderAdapter.kt
  28. 24 0
      app/src/main/java/com/khmer9/yuki/adapter/DepositStatementAdapter.kt
  29. 126 0
      app/src/main/java/com/khmer9/yuki/adapter/DepositStatementHeaderAdapter.kt
  30. 23 0
      app/src/main/java/com/khmer9/yuki/adapter/ResultStatementAdapter.kt
  31. 93 0
      app/src/main/java/com/khmer9/yuki/adapter/ResultStatementHeaderAdapter.kt
  32. 22 0
      app/src/main/java/com/khmer9/yuki/adapter/WithdrawStatementAdapter.kt
  33. 127 0
      app/src/main/java/com/khmer9/yuki/adapter/WithdrawStatementHeaderAdapter.kt
  34. 119 0
      app/src/main/java/com/khmer9/yuki/app/App.kt
  35. 41 0
      app/src/main/java/com/khmer9/yuki/app/ErrorInterceptor.kt
  36. 14 0
      app/src/main/java/com/khmer9/yuki/app/ExceptionExtension.kt
  37. 22 0
      app/src/main/java/com/khmer9/yuki/app/ServiceInterceptor.kt
  38. 135 0
      app/src/main/java/com/khmer9/yuki/app/ShareActivity.kt
  39. 470 0
      app/src/main/java/com/khmer9/yuki/app/ShareViewModel.kt
  40. 21 0
      app/src/main/java/com/khmer9/yuki/app/ShareViewState.kt
  41. 41 0
      app/src/main/java/com/khmer9/yuki/base/BaseActivity.kt
  42. 186 0
      app/src/main/java/com/khmer9/yuki/base/BaseFragment.kt
  43. 16 0
      app/src/main/java/com/khmer9/yuki/base/BaseViewModel.kt
  44. 209 0
      app/src/main/java/com/khmer9/yuki/extension/ViewExtensions.kt
  45. 55 0
      app/src/main/java/com/khmer9/yuki/remote/CreateBettingDataModel.kt
  46. 67 0
      app/src/main/java/com/khmer9/yuki/remote/CurrentBettingDataModel.kt
  47. 79 0
      app/src/main/java/com/khmer9/yuki/remote/CurrentBettingMobileDataModel.kt
  48. 34 0
      app/src/main/java/com/khmer9/yuki/remote/CurrentGameDataModel.kt
  49. 82 0
      app/src/main/java/com/khmer9/yuki/remote/LoginDataModel.kt
  50. 47 0
      app/src/main/java/com/khmer9/yuki/remote/LottoSetDataModel.kt
  51. 117 0
      app/src/main/java/com/khmer9/yuki/remote/MainDataModel.kt
  52. 93 0
      app/src/main/java/com/khmer9/yuki/remote/PlayDataModel.kt
  53. 70 0
      app/src/main/java/com/khmer9/yuki/remote/PreviousBettingDataModel.kt
  54. 99 0
      app/src/main/java/com/khmer9/yuki/remote/ReportDataBettingModel.kt
  55. 197 0
      app/src/main/java/com/khmer9/yuki/remote/ReportDataModel.kt
  56. 155 0
      app/src/main/java/com/khmer9/yuki/remote/ReportDataResultModel.kt
  57. 73 0
      app/src/main/java/com/khmer9/yuki/remote/ReportDataWithdrawModel.kt
  58. 73 0
      app/src/main/java/com/khmer9/yuki/remote/ReportDepositDataModel.kt
  59. 150 0
      app/src/main/java/com/khmer9/yuki/remote/ResultDataModel.kt
  60. 16 0
      app/src/main/java/com/khmer9/yuki/remote/SocketMessageDataModel.kt
  61. 30 0
      app/src/main/java/com/khmer9/yuki/remote/UpdatePwdDataModel.kt
  62. 46 0
      app/src/main/java/com/khmer9/yuki/remote/YukiSetDataModel.kt
  63. 13 0
      app/src/main/java/com/khmer9/yuki/remote/main/SocketClientNewGameDataModel.kt
  64. 13 0
      app/src/main/java/com/khmer9/yuki/remote/main/SocketClientRandomDataModel.kt
  65. 134 0
      app/src/main/java/com/khmer9/yuki/remote/main/SocketClientResultDataModel.kt
  66. 19 0
      app/src/main/java/com/khmer9/yuki/remote/main/SocketClientTimeDataModel.kt
  67. 56 0
      app/src/main/java/com/khmer9/yuki/remote/service/ApiService.kt
  68. 19 0
      app/src/main/java/com/khmer9/yuki/remote/service/ConnectivityStates.java
  69. 25 0
      app/src/main/java/com/khmer9/yuki/remote/service/EventListener.java
  70. 130 0
      app/src/main/java/com/khmer9/yuki/remote/service/SocketService.java
  71. 19 0
      app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailAdapter.kt
  72. 46 0
      app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailListViewHolder.kt
  73. 44 0
      app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailViewModel.kt
  74. 8 0
      app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailViewState.kt
  75. 81 0
      app/src/main/java/com/khmer9/yuki/screen/detail/BettingDetailDataModel.kt
  76. 123 0
      app/src/main/java/com/khmer9/yuki/screen/detail/BettingDetailFragment.kt
  77. 69 0
      app/src/main/java/com/khmer9/yuki/screen/login/LoginActivity.kt
  78. 68 0
      app/src/main/java/com/khmer9/yuki/screen/login/LoginViewModel.kt
  79. 9 0
      app/src/main/java/com/khmer9/yuki/screen/login/LoginViewState.kt
  80. 21 0
      app/src/main/java/com/khmer9/yuki/screen/main/MainAdapter.kt
  81. 27 0
      app/src/main/java/com/khmer9/yuki/screen/main/MainAdapterHeader.kt
  82. 778 0
      app/src/main/java/com/khmer9/yuki/screen/main/MainFragment.kt
  83. 10 0
      app/src/main/java/com/khmer9/yuki/screen/main/MainListItem.kt
  84. 127 0
      app/src/main/java/com/khmer9/yuki/screen/main/MainListViewHolder.kt
  85. 959 0
      app/src/main/java/com/khmer9/yuki/screen/play/PlayFragment.kt
  86. 50 0
      app/src/main/java/com/khmer9/yuki/screen/report/ReportAdapter.kt
  87. 36 0
      app/src/main/java/com/khmer9/yuki/screen/report/ReportListViewHolder.kt
  88. 23 0
      app/src/main/java/com/khmer9/yuki/screen/report/ReportViewState.kt
  89. 245 0
      app/src/main/java/com/khmer9/yuki/screen/report/StatementFragment.kt
  90. 14 0
      app/src/main/java/com/khmer9/yuki/screen/report/bettingStatement/BettingHeaderViewHolder.kt
  91. 92 0
      app/src/main/java/com/khmer9/yuki/screen/report/bettingStatement/ReportBettingViewHolder.kt
  92. 94 0
      app/src/main/java/com/khmer9/yuki/screen/report/depositStatement/ReportDepositViewHolder.kt
  93. 63 0
      app/src/main/java/com/khmer9/yuki/screen/report/resultStatement/ReportResultViewHolder.kt
  94. 82 0
      app/src/main/java/com/khmer9/yuki/screen/report/withdrawStatement/ReportWithdrawViewHolder.kt
  95. 422 0
      app/src/main/java/com/khmer9/yuki/screen/result/ResultFragment.kt
  96. 10 0
      app/src/main/java/com/khmer9/yuki/screen/result/ResultViewState.kt
  97. 19 0
      app/src/main/java/com/khmer9/yuki/screen/result/Table0Adapter.kt
  98. 11 0
      app/src/main/java/com/khmer9/yuki/screen/result/Table0ListItem.kt
  99. 62 0
      app/src/main/java/com/khmer9/yuki/screen/result/Table0ListViewHolder.kt
  100. 19 0
      app/src/main/java/com/khmer9/yuki/screen/result/Table1Adapter.kt

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+/app/prod/release/Khmer9_Yuki_prodRelease_1.0.1.apk
+/app/prod/release/output-metadata.json

+ 3 - 0
.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 1 - 0
.idea/.name

@@ -0,0 +1 @@
+SampleApp

+ 123 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,123 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <option name="RIGHT_MARGIN" value="200" />
+    <JetCodeStyleSettings>
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </JetCodeStyleSettings>
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+    <codeStyleSettings language="kotlin">
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 5 - 0
.idea/codeStyles/codeStyleConfig.xml

@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>

+ 6 - 0
.idea/compiler.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <bytecodeTargetLevel target="11" />
+  </component>
+</project>

+ 21 - 0
.idea/gradle.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="testRunner" value="GRADLE" />
+        <option name="distributionType" value="DEFAULT_WRAPPED" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+            <option value="$PROJECT_DIR$/printooth" />
+            <option value="$PROJECT_DIR$/sunmiprinterutill" />
+          </set>
+        </option>
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 35 - 0
.idea/jarRepositories.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central repository" />
+      <option name="url" value="https://repo1.maven.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="jboss.community" />
+      <option name="name" value="JBoss Community repository" />
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="BintrayJCenter" />
+      <option name="name" value="BintrayJCenter" />
+      <option name="url" value="https://jcenter.bintray.com/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="Google" />
+      <option name="name" value="Google" />
+      <option name="url" value="https://dl.google.com/dl/android/maven2/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="maven" />
+      <option name="name" value="maven" />
+      <option name="url" value="https://jitpack.io" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="MavenRepo" />
+      <option name="name" value="MavenRepo" />
+      <option name="url" value="https://repo.maven.apache.org/maven2/" />
+    </remote-repository>
+  </component>
+</project>

+ 101 - 0
.idea/misc.xml

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="DesignSurface">
+    <option name="filePathToZoomLevelMap">
+      <map>
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/bet_detail_item.xml" value="0.33" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/betting_detail.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/fragment_play.xml" value="0.2807971014492754" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/fragment_report.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/item_table_holder.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_betting_header.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_betting_statement.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_fragment_main_yuki.xml" value="0.33" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_main_adapter_header.xml" value="0.35260416666666666" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_result_statement.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_result_statement_header.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_withdraw_header.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/Yuki-Mobile/app/src/main/res/layout/layout_withdraw_statement.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.1745" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/drawable/ic_launcher_background.xml" value="0.1745" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/drawable/ic_logo_background.xml" value="0.333" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/activity_splash.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/app_bar.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/fragment_main.xml" value="0.33" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/fragment_play.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/fragment_report.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/fragment_result.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/fragment_setting.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/item_box.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/item_table_holder.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/layout_betting_statement.xml" value="0.36666666666666664" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/layout/layout_withdraw_statement.xml" value="0.25" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/mipmap-anydpi-v26/ic_logo.xml" value="0.1965" />
+        <entry key="..\:/3-Work/Android/lotto80-mobile-android/app/src/main/res/mipmap-anydpi-v26/ic_logo_round.xml" value="0.333" />
+        <entry key="..\:/6-Android/workspace/lotto80-mobile-android/app/src/main/res/layout/activity_login.xml" value="0.22826086956521738" />
+        <entry key="..\:/6-Android/workspace/lotto80-mobile-android/app/src/main/res/layout/item_table_holder.xml" value="0.1" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/fragment_main.xml" value="0.25416666666666665" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/fragment_play.xml" value="0.25416666666666665" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/fragment_report.xml" value="0.25416666666666665" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/item_report.xml" value="0.22826086956521738" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/layout_betting_header.xml" value="0.22826086956521738" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/layout_betting_statement.xml" value="0.25416666666666665" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/layout_deposit_header.xml" value="0.22826086956521738" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/layout_result_statement_header.xml" value="0.22826086956521738" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/layout_withdraw_header.xml" value="0.25416666666666665" />
+        <entry key="..\:/9-Work/17-Freelancer/Khmer9-Lotto-Android/lotto80-mobile-android/app/src/main/res/layout/layout_withdraw_statement.xml" value="0.25416666666666665" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/drawable/bg_circle_aqua.xml" value="0.1615" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/drawable/bg_circle_blue.xml" value="0.1735" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/drawable/ic_logo_background.xml" value="0.196" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/drawable/result_large.xml" value="0.1735" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/drawable/result_small.xml" value="0.1735" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout-v23/fragment_play.xml" value="0.20416666666666666" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/activity_login.xml" value="0.36666666666666664" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/fragment_main.xml" value="0.18802083333333333" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/fragment_play.xml" value="0.335042735042735" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/fragment_play_yuki.xml" value="0.1" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/fragment_result.xml" value="0.22732491389207807" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/invoice_print.xml" value="0.22644927536231885" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/item_box_number.xml" value="0.4030612244897959" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/layout_fragment_main_yuki.xml" value="0.4013683010262258" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/layout/view_square.xml" value="0.18802083333333333" />
+        <entry key="..\:/PartTimeProject/Yuki/Yuki-Mobile/app/src/main/res/mipmap-anydpi-v26/ic_logo.xml" value="0.196" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/activity_login.xml" value="0.15489130434782608" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/app_bar.xml" value="0.25416666666666665" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/fragment_main.xml" value="0.25416666666666665" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/fragment_play.xml" value="0.25416666666666665" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/invoice_print.xml" value="0.25416666666666665" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_betting_header.xml" value="0.17083333333333334" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_betting_statement.xml" value="0.17083333333333334" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_fragment_main_yuki.xml" value="0.25416666666666665" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_result_statement.xml" value="0.17083333333333334" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_result_statement_header.xml" value="0.17083333333333334" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_withdraw_header.xml" value="0.17083333333333334" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/layout_withdraw_statement.xml" value="0.1" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/test.xml" value="0.25416666666666665" />
+        <entry key="..\:/android/Yuki-Mobile/app/src/main/res/layout/view_circle.xml" value="0.25416666666666665" />
+      </map>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ScopeChooserConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+    </states>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 161 - 0
app/build.gradle

@@ -0,0 +1,161 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+// Apply the Crashlytics Gradle plugin
+//apply plugin: 'com.google.gms.google-services'
+//apply plugin: 'com.google.firebase.crashlytics'
+android {
+
+    compileSdkVersion 32
+    signingConfigs {
+        release {
+            storeFile rootProject.file("evaluation-testing.jks")
+            //storeFile file('D:\\Desktop\\evaluation-testing.jks')
+            storePassword "123456"
+            keyAlias = "gdt"
+            keyPassword "123456"
+        }
+    }
+    defaultConfig {
+        applicationId "com.khmer9.yuki"
+        minSdkVersion 21
+        targetSdkVersion 32
+        versionCode 1
+        versionName "1.0.1"
+        signingConfig signingConfigs.release
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        debug {
+            manifestPlaceholders = [enableCrashlytic: false]
+//            firebaseCrashlytics.mappingFileUploadEnabled false
+            minifyEnabled false
+            debuggable true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            manifestPlaceholders = [enableCrashlytic: false]
+//            firebaseCrashlytics.mappingFileUploadEnabled true
+            shrinkResources false
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+// To inline the bytecode built with JVM target 1.8 into
+// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
+
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = "1.8"
+    }
+    androidExtensions {
+        experimental = true
+    }
+    packagingOptions {
+        resources {
+            excludes += ['META-INF/DEPENDENCIES', 'META-INF/LICENSE', 'META-INF/LICENSE.txt', 'META-INF/license.txt', 'META-INF/NOTICE', 'META-INF/NOTICE.txt', 'META-INF/notice.txt', 'META-INF/ASL2.0']
+        }
+    }
+    flavorDimensions 'env'
+    productFlavors {
+        dev {
+            dimension 'env'
+            //applicationIdSuffix = ".dev"
+            versionNameSuffix "-dev"
+        }
+        prod {
+            dimension 'env'
+        }
+    }
+    lint {
+        abortOnError false
+        checkReleaseBuilds false
+    }
+    applicationVariants.all { variant ->
+        variant.outputs.all {
+            //def formattedDate = new Date().format('yyyy-MM-dd HH-mm')
+
+            def appName = "Khmer9_Yuki"
+            def appId = variant.applicationId
+            def versionName = variant.versionName
+            def versionCode = variant.versionCode
+            def flavorName = variant.flavorName // e. g. free
+            def buildType = variant.buildType // e. g. debug
+            def variantName = variant.name // e. g. freeDebug
+
+            def apkName = appName + '_' + variantName + '_' + versionName + '.apk'
+            outputFileName = apkName
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation 'androidx.appcompat:appcompat:1.2.0'
+    implementation 'androidx.core:core-ktx:1.3.2'
+    implementation 'com.google.android.material:material:1.2.1'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
+    implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
+    implementation 'androidx.recyclerview:recyclerview:1.2.1'
+    implementation project(path: ':sunmiprinterutill')
+
+    //Architecture components
+    def lifecycle_version = "2.2.0"
+    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
+    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
+    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
+    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
+
+    //RxJava & RxAndroid
+    implementation "io.reactivex.rxjava2:rxjava:2.2.18"
+    implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
+    implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
+
+    //Retrofit
+    def retrofit_version = "2.9.0"
+    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
+    implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
+    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
+    implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
+    implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.7'
+
+    /*Json Serialize & Deserialize Library*/
+    implementation("com.squareup.moshi:moshi-kotlin:1.9.2")
+    kapt("com.squareup.moshi:moshi-kotlin-codegen:1.9.2")
+
+    //Socket io client
+    implementation('io.socket:socket.io-client:2.0.0') {
+        exclude group: 'org.json', module: 'json'
+    }
+
+    //Logging helper
+    implementation 'com.jakewharton.timber:timber:4.7.1'
+    implementation 'com.google.code.gson:gson:2.8.6'
+
+    //implementation 'com.github.mazenrashed:Printooth:1.2.2'
+    //Lib
+    implementation project(path: ':printooth')
+    implementation 'com.squareup.picasso:picasso:2.71828'
+
+    //Firebase
+    // Import the BoM for the Firebase platform
+//    implementation platform('com.google.firebase:firebase-bom:26.4.0')
+//    implementation 'com.google.firebase:firebase-crashlytics-ktx'
+//    implementation 'com.google.firebase:firebase-analytics-ktx'
+//    implementation 'com.google.firebase:firebase-config-ktx'
+
+    implementation 'nl.dionsegijn:konfetti:1.3.2'
+
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}

+ 39 - 0
app/google-services.json

@@ -0,0 +1,39 @@
+{
+  "project_info": {
+    "project_number": "1051787698407",
+    "project_id": "khmer9lotto",
+    "storage_bucket": "khmer9lotto.appspot.com"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:1051787698407:android:e305f17484f532b9490242",
+        "android_client_info": {
+          "package_name": "com.khmer9.yuki"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "1051787698407-ki8dmfdjpfdq84r9mi3h9l44h62uod3p.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyDFmRHJwml_ByjIL1sVBW4oDSDI4cFCEDA"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "1051787698407-ki8dmfdjpfdq84r9mi3h9l44h62uod3p.apps.googleusercontent.com",
+              "client_type": 3
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 18 - 0
app/prod/debug/output-metadata.json

@@ -0,0 +1,18 @@
+{
+  "version": 2,
+  "artifactType": {
+    "type": "APK",
+    "kind": "Directory"
+  },
+  "applicationId": "com.sambath.loto",
+  "variantName": "processProdDebugResources",
+  "elements": [
+    {
+      "type": "SINGLE",
+      "filters": [],
+      "versionCode": 1,
+      "versionName": "1.0",
+      "outputFile": "App_prodDebug_1.0_1.apk"
+    }
+  ]
+}

+ 116 - 0
app/proguard-rules.pro

@@ -0,0 +1,116 @@
+# 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
+-keep class * implements java.io.Serializable { *; }
+
+-keepattributes *Annotation*
+
+#### OkHttp, Retrofit and Moshi
+-dontwarn okhttp3.**
+-dontwarn retrofit2.Platform$Java8
+-dontwarn okio.**
+-dontwarn javax.annotation.**
+-dontwarn org.jetbrains.annotations.**
+
+#-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl
+-keep class kotlin.Metadata { *; }
+-keepclassmembers class kotlin.Metadata {
+    public <methods>;
+}
+
+#-keepnames @kotlin.Metadata class gdt.com.e_payments.data.model.**
+#-keep class gdt.com.e_payments.data.model.** { *; }
+#-keepclassmembers class gdt.com.e_payments.data.model.** { *; }
+
+-keepclasseswithmembers class * {
+    @retrofit2.http.* <methods>;
+}
+
+-keepclasseswithmembers class * {
+    @com.squareup.moshi.* <methods>;
+}
+-keepclassmembers class * {
+    @com.squareup.moshi.FromJson <methods>;
+    @com.squareup.moshi.ToJson <methods>;
+}
+-keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
+    <fields>;
+    **[] values();
+}
+
+-keep @com.squareup.moshi.JsonQualifier interface *
+-keepnames @com.squareup.moshi.JsonClass class *
+
+# Retain generated target class's synthetic defaults constructor and keep DefaultConstructorMarker's
+# name. We will look this up reflectively to invoke the type's constructor.
+#
+# We can't _just_ keep the defaults constructor because Proguard/R8's spec doesn't allow wildcard
+# matching preceding parameters.
+-keepnames class kotlin.jvm.internal.DefaultConstructorMarker
+-keepclassmembers @com.squareup.moshi.JsonClass @kotlin.Metadata class * {
+    synthetic <init>(...);
+}
+
+# Retain generated JsonAdapters if annotated type is retained.
+-if @com.squareup.moshi.JsonClass class *
+-keep class <1>JsonAdapter {
+    <init>(...);
+    <fields>;
+}
+-if @com.squareup.moshi.JsonClass class **$*
+-keep class <1>_<2>JsonAdapter {
+    <init>(...);
+    <fields>;
+}
+-if @com.squareup.moshi.JsonClass class **$*$*
+-keep class <1>_<2>_<3>JsonAdapter {
+    <init>(...);
+    <fields>;
+}
+-if @com.squareup.moshi.JsonClass class **$*$*$*
+-keep class <1>_<2>_<3>_<4>JsonAdapter {
+    <init>(...);
+    <fields>;
+}
+-if @com.squareup.moshi.JsonClass class **$*$*$*$*
+-keep class <1>_<2>_<3>_<4>_<5>JsonAdapter {
+    <init>(...);
+    <fields>;
+}
+-if @com.squareup.moshi.JsonClass class **$*$*$*$*$*
+-keep class <1>_<2>_<3>_<4>_<5>_<6>JsonAdapter {
+    <init>(...);
+    <fields>;
+}
+
+# RxJava
+-dontwarn org.reactivestreams.FlowAdapters
+-dontwarn org.reactivestreams.**
+-dontwarn java.util.concurrent.Flow.**
+-dontwarn java.util.concurrent.**
+# Firebase Crashlytics
+-keepattributes SourceFile,LineNumberTable        # Keep file names and line numbers.
+-keep public class * extends java.lang.Exception  # Optional: Keep custom exceptions.
+-keep class com.google.firebase.crashlytics. { *; }
+-dontwarn com.google.firebase.crashlytics.
+#-keepnames @kotlin.Metadata class com.gdt.dev.gdtevaluationAdmin.data.remote.model.**
+#-keep class com.gdt.dev.gdtevaluationAdmin.data.remote.model.** { *; }
+#-keepclassmembers class com.gdt.dev.gdtevaluationAdmin.data.remote.model.** { *; }

+ 24 - 0
app/src/androidTest/java/com/khmer9/yuki/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.khmer9.yuki
+
+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.sambath.loto", appContext.packageName)
+    }
+}

+ 6 - 0
app/src/dev/java/com/khmer9/yuki/config/Config.kt

@@ -0,0 +1,6 @@
+package com.khmer9.yuki.config
+
+object Config {
+    const val BASE_URL = "http://159.89.118.124:3090"
+    const val SOCKET_URL = "http://159.89.118.124:3096/yuki-betting"
+}

+ 48 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.khmer9.yuki">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+
+    <meta-data
+        android:name="firebase_crashlytics_collection_enabled"
+        android:value="${enableCrashlytic}" />
+    <application
+        android:name="com.khmer9.yuki.app.App"
+        android:allowBackup="false"
+        android:hardwareAccelerated="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        android:usesCleartextTraffic="true"
+        tools:targetApi="m">
+        <activity
+            android:name="com.khmer9.yuki.app.ShareActivity"
+            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
+            android:label="@string/app_name"
+            android:screenOrientation="sensorPortrait"
+            android:windowSoftInputMode="adjustResize" />
+        <activity
+            android:name="com.khmer9.yuki.screen.login.LoginActivity"
+            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
+            android:label="@string/app_name"
+            android:screenOrientation="sensorPortrait"
+            android:windowSoftInputMode="adjustResize" />
+        <activity
+            android:name="com.khmer9.yuki.screen.splash.SplashScreenActivity"
+            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
+            android:exported="true"
+            android:screenOrientation="sensorPortrait"
+            android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>

BIN
app/src/main/ic_launcher-playstore.png


BIN
app/src/main/ic_logo-playstore.png


+ 47 - 0
app/src/main/java/com/khmer9/yuki/adapter/AbstractAdapter.kt

@@ -0,0 +1,47 @@
+package com.gdtlib.lib.adapter
+
+import android.view.LayoutInflater
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.viewholder.AbstractViewHolder
+
+/**
+ * An abstract class of [RecyclerView.Adapter] to display a list of [T]
+ */
+abstract class AbstractAdapter<T : Any>(
+    protected val inflater: LayoutInflater
+) : RecyclerView.Adapter<AbstractViewHolder<T>>(),
+    AbstractViewHolder.OnClickListener {
+
+    interface OnItemClickListener<T : Any> {
+        fun onItemClick(item: T)
+
+    }
+
+    var listener: OnItemClickListener<T>? = null
+
+    var list: List<T> = ArrayList()
+        set(value) {
+            if (field.isNotEmpty()) (field as ArrayList).clear()
+            (field as ArrayList).addAll(value)
+            notifyDataSetChanged()
+        }
+
+    fun addList(list: List<T>) {
+        if (list.isEmpty()) return
+        (this.list as ArrayList).addAll(list)
+        notifyDataSetChanged()
+    }
+
+    override fun getItemCount(): Int {
+        return list.size
+    }
+
+    override fun onBindViewHolder(holder: AbstractViewHolder<T>, position: Int) {
+        val user = list[position]
+        holder.bindView(user)
+    }
+
+    override fun onClick(position: Int) {
+        listener?.onItemClick(list[position])
+    }
+}

+ 23 - 0
app/src/main/java/com/khmer9/yuki/adapter/AbstractViewHolder.kt

@@ -0,0 +1,23 @@
+package com.gdtlib.lib.viewholder
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+abstract class AbstractViewHolder<T : Any>(
+    view: View,
+    private val listener: OnClickListener?
+) : RecyclerView.ViewHolder(view){
+
+    init {
+        itemView.setOnClickListener {
+            listener?.onClick(adapterPosition)
+        }
+    }
+
+    interface OnClickListener {
+        fun onClick(position: Int)
+    }
+
+    abstract fun bindView(item: T)
+
+}

+ 29 - 0
app/src/main/java/com/khmer9/yuki/adapter/BaseListAdapter.kt

@@ -0,0 +1,29 @@
+package com.gdtlib.lib.adapter
+
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+
+abstract class BaseListAdapter : ListAdapter<BaseListItem, RecyclerView.ViewHolder>(BaseListItem.ITEM_CALLBACK) {
+
+    private var itemClickListener: ((Int) -> Unit)? = null
+    private var loadMoreListener: (() -> Unit)? = null
+
+    fun setItemClickListener(itemClickListener: ((Int) -> Unit)?) {
+        this.itemClickListener = itemClickListener
+    }
+
+    fun setLoadMoreListener(loadMoreListener: (() -> Unit)?) {
+        this.loadMoreListener = loadMoreListener
+    }
+
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+        if (holder is BaseListViewHolder) {
+            holder.bindView(getItem(position))
+            holder.setClickListener(itemClickListener)
+            if (position >= itemCount - 1 - 2) {
+                loadMoreListener?.invoke()
+            }
+        }
+    }
+}

+ 21 - 0
app/src/main/java/com/khmer9/yuki/adapter/BaseListItem.kt

@@ -0,0 +1,21 @@
+package com.gdtlib.lib.adapter
+
+import android.annotation.SuppressLint
+import androidx.recyclerview.widget.DiffUtil
+
+abstract class BaseListItem {
+
+    companion object {
+
+        val ITEM_CALLBACK = object : DiffUtil.ItemCallback<BaseListItem>() {
+            override fun areItemsTheSame(oldItem: BaseListItem, newItem: BaseListItem) =
+                oldItem.getUnique() == newItem.getUnique()
+
+            @SuppressLint("DiffUtilEquals")
+            override fun areContentsTheSame(oldItem: BaseListItem, newItem: BaseListItem) =
+                oldItem == newItem
+        }
+    }
+
+    abstract fun getUnique(): String
+}

+ 44 - 0
app/src/main/java/com/khmer9/yuki/adapter/BaseListViewHolder.kt

@@ -0,0 +1,44 @@
+package com.gdtlib.lib.viewholder
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListItem
+
+abstract class BaseListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+    abstract fun bindView(item: BaseListItem)
+
+    fun setClickListener(clickListener: ((Int) -> Unit)?) {
+        if (clickListener != null) {
+            itemView.setOnClickListener {
+                clickListener(adapterPosition)
+            }
+        } else {
+            itemView.setOnClickListener(null)
+        }
+    }
+
+    companion object {
+        fun getCurrency(currency: Int): String {
+            val short = currency / 1000
+            return "${short}K"
+        }
+
+        fun getBettingId(playNo: String, playDate: String): String {
+            val playID = playNo.subSequence(0, playNo.length - 5)
+            val playTime = playDate.subSequence(playDate.length - 8, playDate.length - 3)
+            return "$playID $playTime"
+        }
+
+        fun convertToTypeOfBetting(type: String): String {
+            return when (type.toLowerCase()) {
+                "u" -> "Small"
+                "o" -> "Large"
+                else -> type
+            }
+        }
+
+
+    }
+
+}

+ 23 - 0
app/src/main/java/com/khmer9/yuki/adapter/BettingStatementAdapter.kt

@@ -0,0 +1,23 @@
+package com.khmer9.yuki.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+import com.khmer9.yuki.screen.report.bettingStatement.ReportBettingViewHolder
+
+class BettingStatementAdapter : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null
+    var detailButtonListener: ((String, String) -> Unit)? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        return ReportBettingViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.layout_betting_statement,
+                parent,
+                false
+            ), printButtonListener, detailButtonListener
+        )
+    }
+}

+ 121 - 0
app/src/main/java/com/khmer9/yuki/adapter/BettingStatementHeaderAdapter.kt

@@ -0,0 +1,121 @@
+package com.khmer9.yuki.adapter
+
+import android.app.DatePickerDialog
+import android.app.DatePickerDialog.OnDateSetListener
+import android.graphics.Color
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.EditText
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.remote.ReportBettingRequest
+import java.text.SimpleDateFormat
+import java.util.*
+
+
+class BettingStatementHeaderAdapter(val turnOver: String?, val totalWinLose: String?) : RecyclerView.Adapter<BettingStatementHeaderAdapter.ViewHolder>() {
+    private val myCalendar: Calendar = Calendar.getInstance()
+    private val shareViewModel = App.injectShareViewModel()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        // inflates the card_view_design view
+        // that is used to hold list item
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.layout_betting_header, parent, false)
+
+        val tvTotalWinLose = view.findViewById<TextView>(R.id.tv_total_win_lose)
+        val tvTurnOver = view.findViewById<TextView>(R.id.tv_turn_over)
+        val tvTWinLose = view.findViewById<TextView>(R.id.tv_t_win_lose)
+        val edtStartDate = view.findViewById<EditText>(R.id.edt_start_date)
+        val edtEndDate = view.findViewById<EditText>(R.id.edt_end_date)
+        val btnSearch = view.findViewById<Button>(R.id.btn_filter)
+
+        //------------------------------- filter search
+
+        val dateStart = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtStartDate)
+        }
+
+        val dateEnd = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtEndDate)
+        }
+
+        edtStartDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateStart,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        edtEndDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateEnd,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        btnSearch.setOnClickListener(View.OnClickListener {
+            val startDate = edtStartDate.text.toString()
+            val endDate = edtEndDate.text.toString()
+            val request = ReportBettingRequest(startDate, endDate, 20, 1)
+
+            shareViewModel.remoteReportBettingStatement(request)
+        })
+
+        //----------------------- set value
+
+        if (this.turnOver != null) {
+            tvTurnOver.text = turnOver
+        } else {
+            tvTurnOver.text = "0"
+        }
+
+        if (this.totalWinLose != null) {
+            tvTotalWinLose.text = totalWinLose
+            if (totalWinLose.toDouble() > 0) {
+                tvTotalWinLose.setTextColor(Color.parseColor("#303F9F"))
+                tvTWinLose.setTextColor(Color.BLACK)
+            } else {
+                tvTotalWinLose.setTextColor(Color.RED)
+                tvTWinLose.setTextColor(Color.BLACK)
+            }
+        } else {
+            tvTotalWinLose.text = "0"
+        }
+
+        return ViewHolder(view)
+    }
+
+    private fun updateLabel(edt: EditText) {
+        val myFormat = "yyyy-MM-dd"
+        val dateFormat = SimpleDateFormat(myFormat, Locale.US)
+        edt.setText(dateFormat.format(myCalendar.time))
+    }
+
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+    }
+
+    override fun getItemCount(): Int {
+        return 1;
+    }
+
+    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
+    }
+}

+ 24 - 0
app/src/main/java/com/khmer9/yuki/adapter/DepositStatementAdapter.kt

@@ -0,0 +1,24 @@
+package com.khmer9.yuki.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+import com.khmer9.yuki.screen.report.depositStatement.ReportDepositViewHolder
+import com.khmer9.yuki.screen.report.withdrawStatement.ReportWithdrawViewHolder
+
+class DepositStatementAdapter : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null
+
+    //use the same layout from the withdraw statement
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        return ReportDepositViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.layout_withdraw_statement,
+                parent,
+                false
+            ), printButtonListener
+        )
+    }
+}

+ 126 - 0
app/src/main/java/com/khmer9/yuki/adapter/DepositStatementHeaderAdapter.kt

@@ -0,0 +1,126 @@
+package com.khmer9.yuki.adapter
+
+import android.app.DatePickerDialog
+import android.app.DatePickerDialog.OnDateSetListener
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.EditText
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.remote.ReportDepositRequest
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import java.text.DecimalFormat
+import java.text.SimpleDateFormat
+import java.util.*
+
+class DepositStatementHeaderAdapter(val totalAmount: String?) : RecyclerView.Adapter<DepositStatementHeaderAdapter.ViewHolder>() {
+    private val myCalendar: Calendar = Calendar.getInstance()
+    private val shareViewModel = App.injectShareViewModel()
+    private lateinit var user: User
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DepositStatementHeaderAdapter.ViewHolder {
+        // inflates the card_view_design view
+        // that is used to hold list item
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.layout_withdraw_header, parent, false)
+
+        val tvTurnOver = view.findViewById<TextView>(R.id.tv_total_amount)
+        val edtStartDate = view.findViewById<EditText>(R.id.edt_start_date)
+        val edtEndDate = view.findViewById<EditText>(R.id.edt_end_date)
+        val btnSearch = view.findViewById<Button>(R.id.btn_filter)
+
+        ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+            user = it.user!!
+        }
+
+        //------------------------------- filter search
+
+        val dateStart = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtStartDate)
+        }
+
+        val dateEnd = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtEndDate)
+        }
+
+        edtStartDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateStart,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        edtEndDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateEnd,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        btnSearch.setOnClickListener(View.OnClickListener {
+            val startDate = edtStartDate.text.toString()
+            val endDate = edtEndDate.text.toString()
+            val request = ReportDepositRequest(startDate, endDate, 20, 1)
+
+            shareViewModel.remoteReportDepositStatement(request)
+        })
+
+        //------------------------------- set value
+        if (totalAmount != null) {
+            //tvTurnOver.text = totalAmount
+            when (user.currencyType) {
+                1 -> {//dollar
+                    tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())} $"
+                }
+                2 -> {//riel
+                    tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())} ៛"
+                }
+                3 -> {//baht
+                    tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())} ฿"
+                }
+            }
+        }
+
+        return DepositStatementHeaderAdapter.ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: DepositStatementHeaderAdapter.ViewHolder, position: Int) {
+    }
+
+    override fun getItemCount(): Int {
+        return 1
+    }
+
+    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
+    }
+
+    private fun updateLabel(edt: EditText) {
+        val myFormat = "yyyy-MM-dd"
+        val dateFormat = SimpleDateFormat(myFormat, Locale.US)
+        edt.setText(dateFormat.format(myCalendar.time))
+    }
+}

+ 23 - 0
app/src/main/java/com/khmer9/yuki/adapter/ResultStatementAdapter.kt

@@ -0,0 +1,23 @@
+package com.khmer9.yuki.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+import com.khmer9.yuki.screen.report.depositStatement.ReportDepositViewHolder
+import com.khmer9.yuki.screen.report.resultStatement.ReportResultViewHolder
+
+class ResultStatementAdapter : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        return ReportResultViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.layout_result_statement,
+                parent,
+                false
+            ), printButtonListener
+        )
+    }
+}

+ 93 - 0
app/src/main/java/com/khmer9/yuki/adapter/ResultStatementHeaderAdapter.kt

@@ -0,0 +1,93 @@
+package com.khmer9.yuki.adapter
+
+import android.app.DatePickerDialog
+import android.app.DatePickerDialog.OnDateSetListener
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.EditText
+import androidx.recyclerview.widget.RecyclerView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.remote.ReportResultRequest
+import java.text.SimpleDateFormat
+import java.util.*
+
+class ResultStatementHeaderAdapter : RecyclerView.Adapter<ResultStatementHeaderAdapter.ViewHolder>() {
+    private val myCalendar: Calendar = Calendar.getInstance()
+    private val shareViewModel = App.injectShareViewModel()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        // inflates the card_view_design view
+        // that is used to hold list item
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.layout_result_statement_header, parent, false)
+
+        val edtStartDate = view.findViewById<EditText>(R.id.edt_start_date)
+        val edtEndDate = view.findViewById<EditText>(R.id.edt_end_date)
+        val btnSearch = view.findViewById<Button>(R.id.btn_filter)
+
+        //------------------------------- filter search
+
+        val dateStart = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtStartDate)
+        }
+
+        val dateEnd = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtEndDate)
+        }
+
+        edtStartDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateStart,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        edtEndDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateEnd,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        btnSearch.setOnClickListener(View.OnClickListener {
+            val startDate = edtStartDate.text.toString()
+            val endDate = edtEndDate.text.toString()
+            val request = ReportResultRequest(startDate, endDate, 20, 1)
+
+            shareViewModel.remoteReportResultStatement(request)
+        })
+
+        return ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+    }
+
+    override fun getItemCount(): Int {
+        return 1
+    }
+
+    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
+    }
+
+    private fun updateLabel(edt: EditText) {
+        val myFormat = "yyyy-MM-dd"
+        val dateFormat = SimpleDateFormat(myFormat, Locale.US)
+        edt.setText(dateFormat.format(myCalendar.time))
+    }
+}

+ 22 - 0
app/src/main/java/com/khmer9/yuki/adapter/WithdrawStatementAdapter.kt

@@ -0,0 +1,22 @@
+package com.khmer9.yuki.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+import com.khmer9.yuki.screen.report.withdrawStatement.ReportWithdrawViewHolder
+
+class WithdrawStatementAdapter : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        return ReportWithdrawViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.layout_withdraw_statement,
+                parent,
+                false
+            ), printButtonListener
+        )
+    }
+}

+ 127 - 0
app/src/main/java/com/khmer9/yuki/adapter/WithdrawStatementHeaderAdapter.kt

@@ -0,0 +1,127 @@
+package com.khmer9.yuki.adapter
+
+import android.app.DatePickerDialog
+import android.app.DatePickerDialog.OnDateSetListener
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.EditText
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.remote.ReportWithdrawRequest
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import java.text.DecimalFormat
+import java.text.SimpleDateFormat
+import java.util.*
+
+class WithdrawStatementHeaderAdapter(val totalAmount: String?) : RecyclerView.Adapter<WithdrawStatementHeaderAdapter.ViewHolder>() {
+    private val myCalendar: Calendar = Calendar.getInstance()
+    private val shareViewModel = App.injectShareViewModel()
+    private lateinit var user: User
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WithdrawStatementHeaderAdapter.ViewHolder {
+        // inflates the card_view_design view
+        // that is used to hold list item
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.layout_withdraw_header, parent, false)
+
+        val tvTurnOver = view.findViewById<TextView>(R.id.tv_total_amount)
+        val edtStartDate = view.findViewById<EditText>(R.id.edt_start_date)
+        val edtEndDate = view.findViewById<EditText>(R.id.edt_end_date)
+        val btnSearch = view.findViewById<Button>(R.id.btn_filter)
+
+        ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+            user = it.user!!
+        }
+
+        //------------------------------- filter search
+
+        val dateStart = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtStartDate)
+        }
+
+        val dateEnd = OnDateSetListener { view, year, month, day ->
+            myCalendar.set(Calendar.YEAR, year)
+            myCalendar.set(Calendar.MONTH, month)
+            myCalendar.set(Calendar.DAY_OF_MONTH, day)
+            updateLabel(edtEndDate)
+        }
+
+        edtStartDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateStart,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        edtEndDate.setOnClickListener(View.OnClickListener {
+            DatePickerDialog(
+                view.context,
+                dateEnd,
+                myCalendar[Calendar.YEAR],
+                myCalendar[Calendar.MONTH],
+                myCalendar[Calendar.DAY_OF_MONTH]
+            ).show()
+        })
+
+        btnSearch.setOnClickListener(View.OnClickListener {
+            val startDate = edtStartDate.text.toString()
+            val endDate = edtEndDate.text.toString()
+            val request = ReportWithdrawRequest(startDate, endDate, 20, 1)
+
+            shareViewModel.remoteReportWithdrawStatement(request)
+        })
+
+        //------------------------------- set value
+        if (totalAmount != null) {
+            //tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())}"
+
+            when (user.currencyType) {
+                1 -> {//dollar
+                    tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())} $"
+                }
+                2 -> {//riel
+                    tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())} ៛"
+                }
+                3 -> {//baht
+                    tvTurnOver.text = "${currencyFormat(totalAmount.toDouble())} ฿"
+                }
+            }
+        }
+
+        return WithdrawStatementHeaderAdapter.ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: WithdrawStatementHeaderAdapter.ViewHolder, position: Int) {
+    }
+
+    override fun getItemCount(): Int {
+        return 1
+    }
+
+    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
+    }
+
+    private fun updateLabel(edt: EditText) {
+        val myFormat = "yyyy-MM-dd"
+        val dateFormat = SimpleDateFormat(myFormat, Locale.US)
+        edt.setText(dateFormat.format(myCalendar.time))
+    }
+}

+ 119 - 0
app/src/main/java/com/khmer9/yuki/app/App.kt

@@ -0,0 +1,119 @@
+package com.khmer9.yuki.app
+
+import android.app.Application
+import android.content.Context
+import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor
+import com.khmer9.yuki.BuildConfig
+import com.khmer9.yuki.config.Config
+import com.khmer9.yuki.remote.service.ApiService
+import com.khmer9.yuki.remote.service.SocketService
+import com.khmer9.yuki.util.ModelPreferencesManager
+import com.khmer9.yuki.util.PrefHelper
+import com.khmer9.yuki.util.UnsafeOkHttpClient
+import com.khmer9.yuki.util.screenRectPx
+import com.mazenrashed.printooth.Printooth
+import com.squareup.moshi.Moshi
+import com.squareup.picasso.Picasso
+import okhttp3.OkHttpClient
+import retrofit2.CallAdapter
+import retrofit2.Converter
+import retrofit2.Retrofit
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
+import retrofit2.converter.moshi.MoshiConverterFactory
+import timber.log.Timber
+import java.util.concurrent.TimeUnit
+
+class App : Application() {
+    companion object {
+        private lateinit var retrofit: Retrofit
+        private lateinit var apiService: ApiService
+        private lateinit var socketService: SocketService
+        private lateinit var picasso: Picasso
+        private lateinit var shareViewModel: ShareViewModel
+        private lateinit var prefHelper: PrefHelper
+        var guidelineLeft: Float = 0f
+        var guidelineRight: Float = 100f
+
+        fun injectApiService() = apiService
+        fun injectShareViewModel() = shareViewModel
+        fun injectPrefHelper() = prefHelper
+
+        // Called on main activity create
+        fun onAppStart(token: String) {
+            // Log.d("Socket", "onAppStart")
+            shareViewModel.startListeningToSocketServer(token)
+        }
+
+        // Called on main activity destroy
+        fun onAppStop() {
+            //Log.d("Socket", "onAppStop")
+            shareViewModel.stopListeningToSocketServer()
+        }
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        ModelPreferencesManager.with(this)
+        Printooth.init(this)
+        picasso = Picasso.Builder(this).loggingEnabled(true).build()
+        // FirebaseApp.initializeApp(this)
+        Picasso.setSingletonInstance(picasso)
+
+        if (BuildConfig.DEBUG) {
+            Timber.uprootAll()
+            Timber.plant(Timber.DebugTree())
+        }
+
+        val widthPx = screenRectPx.width()
+        val heightPx = screenRectPx.height()
+        val percentage = widthPx * 100 / heightPx
+
+        when (percentage) {
+            in 30..54 -> {
+                guidelineLeft = 0f
+                guidelineRight = 1f
+            }
+            else -> {
+                guidelineLeft = 0.05f
+                guidelineRight = 0.95f
+            }
+        }
+
+        prefHelper = PrefHelper(context = appContext(this))
+        val builder = OkHttpClient.Builder()
+
+        if (BuildConfig.DEBUG) {
+            builder
+                .addInterceptor(ServiceInterceptor(prefHelper))
+                .addInterceptor(OkHttpProfilerInterceptor())
+                .addInterceptor(ErrorInterceptor(appContext(this), prefHelper))
+                .readTimeout(5, TimeUnit.MINUTES)
+                .writeTimeout(60, TimeUnit.SECONDS)
+
+        } else {
+            builder
+                .addInterceptor(ServiceInterceptor(prefHelper))
+                .addInterceptor(ErrorInterceptor(appContext(this), prefHelper))
+                .readTimeout(5, TimeUnit.MINUTES)
+                .writeTimeout(60, TimeUnit.SECONDS)
+        }
+
+        val client = builder.build()
+        retrofit = Retrofit.Builder()
+            .baseUrl(Config.BASE_URL)
+            .client(client)
+            .addCallAdapterFactory(callFactory())
+            .addConverterFactory(converterFactory(moshi()))
+            .build()
+
+        apiService = retrofit.create(ApiService::class.java)
+        socketService = SocketService()
+        shareViewModel = ShareViewModel(apiService, prefHelper, socketService)
+    }
+
+    private fun callFactory(): CallAdapter.Factory = RxJava2CallAdapterFactory.create()
+    private fun converterFactory(moshi: Moshi): Converter.Factory = MoshiConverterFactory.create(moshi).withNullSerialization()
+    private fun moshi(): Moshi = Moshi.Builder().build()
+    private fun appContext(app: App): Context = app
+    fun unSafeOkHttpClient(): OkHttpClient = UnsafeOkHttpClient.getClient()
+}

+ 41 - 0
app/src/main/java/com/khmer9/yuki/app/ErrorInterceptor.kt

@@ -0,0 +1,41 @@
+package com.khmer9.yuki.app
+
+import android.content.Context
+import android.content.Intent
+import com.khmer9.yuki.screen.login.LoginActivity
+import com.khmer9.yuki.util.PrefHelper
+import okhttp3.Interceptor
+import okhttp3.Request
+import okhttp3.Response
+
+class ErrorInterceptor(private val context: Context, private val prefHelper: PrefHelper) : Interceptor {
+    override fun intercept(chain: Interceptor.Chain): Response {
+
+        val request: Request = chain.request()
+        val response = chain.proceed(request)
+        when (response.code) {
+//            400 -> {
+//                //Show Bad Request Error Message
+//            }
+            401 -> {
+                //Show UnauthorizedError Message
+                    prefHelper.logout()
+                    val i =  Intent(context, LoginActivity::class.java)
+                    i.flags=Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
+                    context.startActivity(i)
+            }
+
+//            403 -> {
+//                //Show Forbidden Message
+//            }
+//
+//            404 -> {
+//                //Show NotFound Message
+//            }
+
+            // ... and so on
+
+        }
+        return response
+    }
+}

+ 14 - 0
app/src/main/java/com/khmer9/yuki/app/ExceptionExtension.kt

@@ -0,0 +1,14 @@
+package com.khmer9.yuki.app
+
+import java.net.ConnectException
+import java.net.SocketTimeoutException
+import java.util.concurrent.TimeoutException
+
+fun Throwable.getErrorCode(): String {
+    return when (this) {
+        is SocketTimeoutException -> "[120]"
+        is ConnectException -> "[120]"
+        is TimeoutException -> "[121]"
+        else -> "[122]"
+    }
+}

+ 22 - 0
app/src/main/java/com/khmer9/yuki/app/ServiceInterceptor.kt

@@ -0,0 +1,22 @@
+package com.khmer9.yuki.app
+
+import com.khmer9.yuki.util.PrefHelper
+import okhttp3.Interceptor
+import okhttp3.Response
+
+class ServiceInterceptor(private val prefHelper: PrefHelper) : Interceptor {
+    override fun intercept(chain: Interceptor.Chain): Response {
+        var request = chain.request()
+
+        if (request.header("No-Authentication") == null) {
+            //or use Token Function
+            if (prefHelper.getToken().isNotEmpty()) {
+                val finalToken = "${prefHelper.getToken()}"
+                request = request.newBuilder()
+                    .addHeader("Authorization", "bearer " + finalToken)
+                    .build()
+            }
+        }
+        return chain.proceed(request)
+    }
+}

+ 135 - 0
app/src/main/java/com/khmer9/yuki/app/ShareActivity.kt

@@ -0,0 +1,135 @@
+package com.khmer9.yuki.app
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import androidx.constraintlayout.widget.Group
+import androidx.core.view.isVisible
+//import com.google.firebase.ktx.Firebase
+//import com.google.firebase.remoteconfig.FirebaseRemoteConfig
+//import com.google.firebase.remoteconfig.ktx.remoteConfig
+//import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
+import com.khmer9.yuki.R
+import com.khmer9.yuki.base.BaseActivity
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.screen.main.MainFragment
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import com.khmer9.yuki.util.PrefHelper
+
+class ShareActivity : BaseActivity() {
+    lateinit var settingImageView: ImageView
+    lateinit var reportImageView: ImageView
+    lateinit var soundImageView: ImageView
+    lateinit var userNameTextView: TextView
+    lateinit var balanceTextView: TextView
+    lateinit var refreshImageView: ImageView
+    lateinit var loginData: LoginData
+    lateinit var groupToolbarIcons: Group
+    lateinit var sharePref: PrefHelper
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+            loginData = it
+        }
+
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+        sharePref = App.injectPrefHelper()
+
+        if (savedInstanceState == null) {
+            supportFragmentManager.beginTransaction()
+                .add(
+                    R.id.container,
+                    MainFragment().apply {
+                        arguments =
+                            Bundle().apply {
+                                //putInt(InventoryFragment.COM_INTO_ID_KEY,10)
+                            }
+
+                    })
+                .addToBackStack(null)
+                .commit()
+        }
+
+        reportImageView = findViewById<ImageView>(R.id.iv_report)
+        settingImageView = findViewById<ImageView>(R.id.iv_setting)
+        soundImageView = findViewById<ImageView>(R.id.iv_sound)
+        refreshImageView = findViewById<ImageView>(R.id.iv_auto_new)
+        groupToolbarIcons = findViewById<Group>(R.id.group_toolbar_icons)
+        userNameTextView = findViewById<TextView>(R.id.tv_user_id)
+        balanceTextView = findViewById<TextView>(R.id.tv_amount)
+
+        soundImageView.setOnClickListener {
+            if (sharePref.getIsMute()) {
+                sharePref.setIsMute(false)
+                soundImageView.setImageResource(R.drawable.ic_unmute)
+            } else {
+                sharePref.setIsMute(true)
+                soundImageView.setImageResource(R.drawable.ic_mute)
+            }
+        }
+
+        if (sharePref.getIsMute()) {
+            soundImageView.setImageResource(R.drawable.ic_mute)
+        } else {
+            soundImageView.setImageResource(R.drawable.ic_unmute)
+        }
+    }
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        super.onWindowFocusChanged(hasFocus)
+        //Log.d("Window", "hase focus")
+        if (hasFocus) hideSystemUI()
+    }
+
+    private fun hideSystemUI() {
+        window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                // Set the content to appear under the system bars so that the
+                // content doesn't resize when the system bars hide and show.
+                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                // Hide the nav bar and status bar
+                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                or View.SYSTEM_UI_FLAG_FULLSCREEN)
+    }
+
+    override fun onBackPressed() {
+        val count = supportFragmentManager.backStackEntryCount
+        when (count) {
+            1 -> {
+                finish()
+            }
+            else -> {
+                groupToolbarIcons.isVisible = true
+                super.onBackPressed()
+            }
+        }
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+    }
+
+    private var confirmDialog: AlertDialog? = null
+
+    override fun onPause() {
+        super.onPause()
+        App.onAppStop()
+        //Log.d("Lifecycle","onPause")
+    }
+
+    override fun onResume() {
+        super.onResume()
+        App.onAppStart(token = loginData.token.toString())
+        //Log.d("Lifecycle","onResume")
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+    }
+}

+ 470 - 0
app/src/main/java/com/khmer9/yuki/app/ShareViewModel.kt

@@ -0,0 +1,470 @@
+package com.khmer9.yuki.app
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.khmer9.yuki.base.BaseViewModel
+import com.khmer9.yuki.remote.*
+import com.khmer9.yuki.remote.service.ApiService
+import com.khmer9.yuki.remote.service.ConnectivityStates
+import com.khmer9.yuki.remote.service.EventListener
+import com.khmer9.yuki.remote.service.SocketService
+import com.khmer9.yuki.screen.report.ReportViewState
+import com.khmer9.yuki.screen.result.ResultViewState
+import com.khmer9.yuki.util.Event
+import com.khmer9.yuki.util.PrefHelper
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+import org.json.JSONObject
+import java.net.URISyntaxException
+import java.util.concurrent.TimeUnit
+
+class ShareViewModel(
+    private val apiService: ApiService,
+    private val prefHelper: PrefHelper,
+    private val socketService: SocketService
+) : BaseViewModel(), EventListener {
+
+    //Socket Connection Live Data
+    private val connectivityStateLD = MutableLiveData<Int>()
+    val getConnectivityStates: LiveData<Int> get() = connectivityStateLD
+
+    //MainScreen and Create Betting Screen
+    private val shareState = MutableLiveData(ShareViewState())
+    val getShareState: LiveData<ShareViewState> = shareState
+
+    //---------------------------------- New Live Data
+
+    //Result Screen Live Data
+    private val resultState = MutableLiveData<ResultViewState>(ResultViewState())
+    val getResultState: LiveData<ResultViewState> = resultState
+
+    //Report Screen Live Data
+    private val reportState = MutableLiveData<ReportViewState>(ReportViewState())
+    val getReportState: LiveData<ReportViewState> = reportState
+
+    //-----------------------------------------------------Report statement----------------------------------------------------------
+    fun remoteReportBettingStatement(reportBettingRequest: ReportBettingRequest? = null) {
+        if (reportState.value!!.isProgress) return
+        reportState.value = prevReport().copy(isProgress = true)
+
+        var request = ReportBettingRequest("", "", 20, 1)
+
+        if (reportBettingRequest == null) {
+            request = ReportBettingRequest("", "", 20, 1)
+        } else {
+            request = reportBettingRequest
+        }
+
+        disposables.add(
+            apiService.remoteBettingScreen(request)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            reportState.value = prevReport().copy(
+                                isProgress = false,
+                                statementList = it.statementList,
+                                statementListTurnOver = it.turnOver,
+                                statementListAmountWinLose = it.amountWinLose,
+                                error = null
+                            )
+                        }
+                    } else {
+                        reportState.value = prevReport().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    reportState.value = prevReport().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    fun remoteReportWithdrawStatement(reportWithdrawRequest: ReportWithdrawRequest? = null) {
+        if (reportState.value!!.isProgress) return
+
+        var request = ReportWithdrawRequest("", "", 20, 1)
+
+        if (reportWithdrawRequest == null) {
+            request = ReportWithdrawRequest("", "", 20, 1)
+        } else {
+            request = reportWithdrawRequest
+        }
+
+        reportState.value = prevReport().copy(isProgress = true)
+        disposables.add(
+            apiService.remoteWithdrawScreen(request)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            reportState.value = prevReport().copy(
+                                isProgress = false,
+                                statementWithdrawList = it.statementWithdrawList,
+                                statementWithdrawTotalAmount = it.totalAmount,
+                                error = null
+                            )
+                        }
+                    } else {
+                        reportState.value = prevReport().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    reportState.value = prevReport().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    fun remoteReportDepositStatement(reportDepositRequest: ReportDepositRequest? = null) {
+        if (reportState.value!!.isProgress) return
+
+        var request = ReportDepositRequest("", "", 20, 1)
+
+        if (reportDepositRequest == null) {
+            request = ReportDepositRequest("", "", 20, 1)
+        } else {
+            request = reportDepositRequest
+        }
+
+        reportState.value = prevReport().copy(isProgress = true)
+        disposables.add(
+            apiService.remoteDepositScreen(request)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            reportState.value = prevReport().copy(
+                                isProgress = false,
+                                statementDepositList = it.statementDepositList,
+                                statementDepositTotalAmount = it.totalAmount,
+                                error = null
+                            )
+                        }
+                    } else {
+                        reportState.value = prevReport().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    reportState.value = prevReport().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    fun remoteReportResultStatement(reportResultRequest: ReportResultRequest? = null) {
+        if (reportState.value!!.isProgress) return
+
+        var request = ReportResultRequest("", "", 20, 1)
+
+        if (reportResultRequest == null) {
+            request = ReportResultRequest("", "", 20, 1)
+        } else {
+            request = reportResultRequest
+        }
+
+        reportState.value = prevReport().copy(isProgress = true)
+        disposables.add(
+            apiService.remoteResultStatementScreen(request)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            reportState.value = prevReport().copy(
+                                isProgress = false,
+                                statementResultList = it.resultList,
+                                error = null
+                            )
+                        }
+                    } else {
+                        reportState.value = prevReport().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    reportState.value = prevReport().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    //-----------------------------------------------------End Report Statement----------------------------------------------------------
+
+    fun remoteResult() {
+        if (resultState.value!!.isProgress) return
+        resultState.value = prevResult().copy(isProgress = true)
+
+        disposables.add(
+            apiService.remoteResultScreen()
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            Log.d("remoteResult", "original :: " + it)
+                            val sortedList = it.sortedWith(compareBy { it.no })
+                            Log.d("remoteResult", "sort :: " + sortedList)
+                            resultState.value = prevResult().copy(
+                                isProgress = false,
+                                resultList = sortedList,
+                                error = null
+                            )
+                        }
+                    } else {
+                        resultState.value = prevResult().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    resultState.value = prevResult().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    fun remoteBettingCurrentMobileAPI() {
+        shareState.value = prevShare().copy(
+            initial = false,
+            isProgress = true,
+            error = null,
+            isAutoPrint = null,
+            accountBalance = null,
+            previousBettingList = null,
+            //myBettingMobileList = null,
+        )
+        disposables.add(
+            apiService.remoteCurrentBettingMobile()
+                .timeout(20, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        Log.d("previousBettingAPI", response.data.toString())
+                        response.data?.let { it ->
+                            shareState.value = prevShare().copy(
+                                isProgress = false,
+                                previousBettingList = it.list as MutableList<CurrentBettingMobile>?,//it.one as MutableList<PreviousBettingList>?,
+                                error = null,
+                                accountBalance = it.balance.toString(),
+                                isAutoPrint = Event(prefHelper.getIsAutoPrint())
+                            )
+                        }
+                    } else {
+                        Log.d("myBettingMobileListAPI", response.message.description)
+                        shareState.value = prevShare().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    Log.d("myBettingMobileListAPI", message)
+                    shareState.value = prevShare().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    fun remoteCreateBettingAPI(lottoSet: List<LottoSet>, amount: Int) {
+        if (shareState.value!!.isProgress) return
+        var requestList: List<CreateBettingRequest> = listOf()
+
+        for (item in lottoSet) {
+            val request = CreateBettingRequest(lottoId = item.id, amount = amount)
+            requestList += request
+        }
+
+        shareState.value = prevShare().copy(
+            initial = false,
+            isProgress = true,
+            error = null,
+            isAutoPrint = null,
+            isBetSuccess = false,
+            betError = null,
+            previousBettingList = null
+        )
+        disposables.add(
+            apiService.createBetting(requestList)
+                .timeout(20, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        Log.d("bettingSuccess", response.data.toString())
+                        shareState.value = prevShare().copy(isProgress = false, isBetSuccess = true, createBettingData = response.data, betError = null, error = null)
+                        remoteBettingCurrentMobileAPI()
+                    } else {
+                        shareState.value = prevShare().copy(isProgress = false, isBetSuccess = false, betError = "[${response.message.description}]", error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    shareState.value = prevShare().copy(isProgress = false, isBetSuccess = false, betError = message, error = message)
+                })
+        )
+    }
+
+    fun remoteMainScreenAPI() {
+        if (shareState.value!!.isProgress) return
+        shareState.value = prevShare().copy(
+            initial = false,
+            isProgress = true,
+            error = null,
+            //myBettingMobileList = null,
+            accountBalance = null,
+            nextNo = null,
+        )
+
+        //remoteCurrentBetting()
+        remoteCurrentGameApi()
+
+        disposables.add(
+            apiService.remoteResultScreen()
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            val lastResultData = it.first()
+                            shareState.value = prevShare().copy(
+                                lastResult = lastResultData.smallLarge.name + " " + lastResultData.range.name + " " + lastResultData.year.name
+                            )
+                        }
+                    } else {
+                        resultState.value = prevResult().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    resultState.value = prevResult().copy(isProgress = false, error = message)
+                })
+        )
+
+        remoteBettingCurrentMobileAPI()
+    }
+
+    fun remoteCurrentGameApi() {
+        disposables.add(
+            apiService.remoteCurrentGame()
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        response.data?.let {
+                            shareState.value = prevShare().copy(
+                                nextNo = it.date + "#" + it.no,
+                            )
+                            Log.d("remoteCurrent", it.toString())
+                        }
+                    } else {
+                        shareState.value = prevShare().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    shareState.value = prevShare().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    /*private fun remoteCurrentBetting() {
+        disposables.add(
+            apiService.remoteCurrentBetting()
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({ response ->
+                    if (response.resultCode == "1") {
+                        Log.d("previousBettingAPI2", response.data.toString())
+                        response.data?.let {
+                            shareState.value = prevShare().copy(
+                                isProgress = false,
+                                myBettingMobileList = it.currentBettingList as MutableList<CurrentBettingList>?,
+                                //accountBalance = it.balance,
+                                error = null
+                            )
+                        }
+                    } else {
+                        shareState.value = prevShare().copy(isProgress = false, error = "[${response.message.description}]")
+                    }
+
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    shareState.value = prevShare().copy(isProgress = false, error = message)
+                })
+        )
+    }*/
+
+    // Remote
+    fun startListeningToSocketServer(token: String) {
+        //if(prefHelper.getToken().isEmpty()) return
+        try {
+            socketService.registerListener(this)
+            socketService.startListening(token)
+        } catch (e: URISyntaxException) {
+            e.printStackTrace()
+        }
+    }
+
+    fun emitCfCreateBetting(createBettingObject: JSONObject) {
+        socketService.startEmitCfCreateBetting(createBettingObject)
+    }
+
+    fun stopListeningToSocketServer() {
+        socketService.unregisterListener(this)
+        socketService.stopListening()
+    }
+
+
+    // Server callbacks
+    override fun onConnect() {
+        connectivityStateLD.postValue(ConnectivityStates.STATE_CONNECTED)
+    }
+
+    override fun onReconnectionTimeout() {
+        connectivityStateLD.postValue(ConnectivityStates.STATE_TIMEOUT)
+    }
+
+    override fun onConnectionError() {
+        connectivityStateLD.postValue(ConnectivityStates.STATE_NOT_CONNECTED)
+    }
+
+    override fun onReconnect() {
+        connectivityStateLD.postValue(ConnectivityStates.STATE_RECONNECTING)
+    }
+
+    override fun onDisconnect() {
+        connectivityStateLD.postValue(ConnectivityStates.STATE_DISCONNECTED)
+    }
+
+    private val clientNewGameState = MutableLiveData<String>()
+    val getClientNewGame: LiveData<String> get() = clientNewGameState
+    override fun onClientNewGameListener(data: String?) {
+        clientNewGameState.postValue(data)
+    }
+
+    private val clientRandomState = MutableLiveData<String>()
+    val getClientRandom: LiveData<String> get() = clientRandomState
+    override fun onClientRandomListener(data: String?) {
+        clientRandomState.postValue(data)
+    }
+
+    override fun onClientResetListener(data: String?) {
+    }
+
+    private val clientResultState = MutableLiveData<String>()
+    val getClientResult: LiveData<String> get() = clientResultState
+    override fun onClientResultListener(data: String?) {
+        clientResultState.postValue(data)
+    }
+
+    override fun onClientUpdateInfoListener(data: String?) {
+    }
+
+    private val clientTimeState = MutableLiveData<String>()
+    val getClientTime: LiveData<String> get() = clientTimeState
+    override fun onClientTimeListener(data: String?) {
+        clientTimeState.postValue(data)
+    }
+
+    private fun prevShare() = shareState.value!!
+
+    private fun prevResult() = resultState.value!!
+
+    private fun prevReport() = reportState.value!!
+}

+ 21 - 0
app/src/main/java/com/khmer9/yuki/app/ShareViewState.kt

@@ -0,0 +1,21 @@
+package com.khmer9.yuki.app
+
+import com.khmer9.yuki.remote.CreateBettingData
+import com.khmer9.yuki.remote.CurrentBettingList
+import com.khmer9.yuki.remote.CurrentBettingMobile
+import com.khmer9.yuki.remote.PreviousBettingList
+import com.khmer9.yuki.util.Event
+
+data class ShareViewState(
+    val initial: Boolean = true,
+    val isProgress: Boolean = false,
+    val error: String? = null,
+    val nextNo: String? = null,
+    val previousBettingList: MutableList<CurrentBettingMobile>? = null,
+    val accountBalance: String? = null,
+    val isAutoPrint: Event<Boolean>? = null,
+    var createBettingData: CreateBettingData? = null,
+    var isBetSuccess: Boolean = false,
+    val betError: String? = null,
+    val lastResult: String? = null,
+)

+ 41 - 0
app/src/main/java/com/khmer9/yuki/base/BaseActivity.kt

@@ -0,0 +1,41 @@
+package com.khmer9.yuki.base
+
+import android.os.Bundle
+import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
+import com.khmer9.yuki.R
+
+abstract class BaseActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        if (savedInstanceState == null) overridePendingTransition(
+            R.anim.slide_in,
+            R.anim.slide_out
+        )
+    }
+
+    override fun finish() {
+        super.finish()
+        overridePendingTransition(
+            R.anim.slide_in_exit,
+            R.anim.slide_out_exit
+        )
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        if (item.itemId == android.R.id.home) {
+            onBackPressed()
+            return true
+        }
+        return super.onOptionsItemSelected(item)
+    }
+
+    override fun onBackPressed() {
+        super.onBackPressed()
+        overridePendingTransition(
+            R.anim.slide_in_exit,
+            R.anim.slide_out_exit
+        )
+    }
+}

+ 186 - 0
app/src/main/java/com/khmer9/yuki/base/BaseFragment.kt

@@ -0,0 +1,186 @@
+package com.khmer9.yuki.base
+
+import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.annotation.LayoutRes
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.Guideline
+import androidx.fragment.app.Fragment
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.remote.LottoSet
+import com.mazenrashed.printooth.Printooth
+import com.mazenrashed.printooth.data.printable.Printable
+import com.mazenrashed.printooth.data.printable.TextPrintable
+import com.mazenrashed.printooth.data.printer.DefaultPrinter
+import com.mazenrashed.printooth.utilities.Printing
+import java.text.DecimalFormat
+import java.text.SimpleDateFormat
+import java.util.*
+
+abstract class BaseFragment(@LayoutRes private val layoutRes: Int) : Fragment() {
+    private var printing: Printing? = null
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        if (Printooth.hasPairedPrinter()) printing = Printooth.printer()
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(layoutRes, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        /*val guideLeft = view.findViewById<Guideline>(R.id.left_guideline)
+        val guideRight = view.findViewById<Guideline>(R.id.right_guideline)
+        val leftParams = guideLeft.layoutParams as ConstraintLayout.LayoutParams
+        leftParams.guidePercent = App.guidelineLeft
+        val rightParams = guideRight.layoutParams as ConstraintLayout.LayoutParams
+        rightParams.guidePercent = App.guidelineRight
+        guideRight.layoutParams = rightParams*/
+    }
+
+    fun currencyFormat(amount: Int): String? {
+        val formatter = DecimalFormat("#,###")
+        return formatter.format(amount)
+    }
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    @SuppressLint("SimpleDateFormat")
+    open fun formatShowDate(showDate: Date): String {
+        val formatter = SimpleDateFormat(("dd/MM/yyyy HH:mm:ss"))
+        return formatter.format(showDate)
+    }
+
+    fun findLottoSet(lottoSet: List<LottoSet>, type_of_betting: String): LottoSet {
+        return lottoSet.find { type_of_betting.equals(it.typeOfBetting, ignoreCase = true) }!!
+    }
+
+    fun animateTextView(initialValue: Int, finalValue: Int, textview: TextView): ValueAnimator {
+        val valueAnimator = ValueAnimator.ofInt(initialValue, finalValue)
+        valueAnimator.duration = 50
+        valueAnimator.addUpdateListener { animator ->
+            textview.text = animator.animatedValue.toString()
+        }
+        valueAnimator.start()
+        return valueAnimator
+
+
+    }
+
+    fun printSomePrintable(userName: String, ticketID: String, gameID: String, betDate: String, betType: String, betCast: String, winLose: String) {
+        if (Printooth.hasPairedPrinter() && printing == null) printing = Printooth.printer()
+        if (printing == null) {
+            Toast.makeText(requireContext(), "No printing paired", Toast.LENGTH_SHORT).show()
+        } else {
+            val printables = getSomePrintables(userName, ticketID, gameID, betDate, betType, betCast, winLose)
+            printing?.print(printables)
+        }
+    }
+
+    private fun getSomePrintables(userName: String, ticketID: String, gameID: String, betDate: String, betType: String, betCast: String, winLose: String) = ArrayList<Printable>().apply {
+
+        add(
+            TextPrintable.Builder()
+                .setText(userName)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setAlignment(DefaultPrinter.ALIGNMENT_CENTER)
+                .setLineSpacing(DefaultPrinter.LINE_SPACING_30)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+                .build()
+        )
+
+        add(
+            TextPrintable.Builder()
+                .setText("----------------")
+                .setLineSpacing(DefaultPrinter.LINE_SPACING_30)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC1252)
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("Ticket ID:   $ticketID")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("Game ID  :   $gameID")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("Date     :   $betDate")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("Bet Type :   $betType")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("Bet Point:   $betCast")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("Win/Lose :   $winLose")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC850)
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("----------------")
+                .setLineSpacing(DefaultPrinter.LINE_SPACING_30)
+                .build()
+        )
+        add(
+            TextPrintable.Builder()
+                .setText("www.mohasombath.com")
+                .setEmphasizedMode(DefaultPrinter.EMPHASIZED_MODE_NORMAL)
+                .setAlignment(DefaultPrinter.ALIGNMENT_CENTER)
+                .setFontSize(DefaultPrinter.FONT_SIZE_NORMAL)
+                .setCharacterCode(DefaultPrinter.CHARCODE_PC437)
+                .setNewLinesAfter(1)
+                .build()
+        )
+
+    }
+}

+ 16 - 0
app/src/main/java/com/khmer9/yuki/base/BaseViewModel.kt

@@ -0,0 +1,16 @@
+package com.khmer9.yuki.base
+
+import androidx.lifecycle.ViewModel
+import io.reactivex.disposables.CompositeDisposable
+
+
+abstract class BaseViewModel : ViewModel() {
+
+    protected val disposables = CompositeDisposable()
+
+    override fun onCleared() {
+        disposables.clear()
+        super.onCleared()
+    }
+
+}

+ 209 - 0
app/src/main/java/com/khmer9/yuki/extension/ViewExtensions.kt

@@ -0,0 +1,209 @@
+package com.khmer9.yuki.extension
+
+import android.graphics.drawable.AnimationDrawable
+import android.os.Handler
+import android.view.View
+import android.view.animation.AlphaAnimation
+import android.view.animation.Animation
+import android.view.animation.LinearInterpolator
+import android.view.animation.OvershootInterpolator
+import com.khmer9.yuki.util.SafeClickListener
+import io.reactivex.Completable
+import java.util.*
+
+fun View.showIf(show: Boolean) {
+    if (show) {
+        show()
+    } else {
+        hide()
+    }
+}
+
+fun View.show() {
+    this.visibility = View.VISIBLE
+}
+
+fun View.hide() {
+    this.visibility = View.INVISIBLE
+}
+
+fun View.slideIn(offset: Float): Completable {
+    return Completable.create {
+        visibility = View.VISIBLE
+        alpha = 0f
+        scaleX = 0f
+        scaleY = 0f
+        translationY = offset
+        animate().alpha(1f)
+            .translationY(0f)
+            .scaleX(1f)
+            .scaleY(1f)
+            .setDuration(200)
+            .setInterpolator(OvershootInterpolator())
+            .withEndAction(it::onComplete)
+    }
+}
+
+fun View.slideOut(offset: Float): Completable {
+    return Completable.create {
+        animate().alpha(0f)
+            .scaleX(0f)
+            .scaleY(0f)
+            .translationY(offset)
+            .setDuration(200)
+            .withEndAction {
+                visibility = View.INVISIBLE
+                it.onComplete()
+            }
+    }
+}
+
+fun View.fadeOut(duration: Long = 30): Completable {
+    return Completable.create {
+        animate().setDuration(duration)
+            .alpha(0f)
+            .withEndAction {
+                visibility = View.GONE
+                it.onComplete()
+            }
+    }
+}
+
+fun View.fadeIn(): Completable {
+    return Completable.create {
+        //visibility = View.VISIBLE
+        alpha = 0f
+        animate().alpha(1f)
+            .setDuration(200)
+            .withEndAction(it::onComplete)
+    }
+}
+
+fun View.rotate(degree: Float): Completable {
+    return Completable.create {
+        rotation=degree
+        animate().rotation(0f)
+            .setDuration(200)
+            .withEndAction(it::onComplete)
+    }
+}
+
+fun View.blink(degree: Float): Completable {
+    return Completable.create {
+        rotation=degree
+        animate().rotation(0f)
+            .setDuration(200)
+            .withEndAction(it::onComplete)
+    }
+}
+fun View.blink(
+    times: Int = Animation.INFINITE,
+    duration: Long = 150L,
+    offset: Long = 20L,
+    minAlpha: Float = 0.0f,
+    maxAlpha: Float = 1.0f,
+    repeatMode: Int = Animation.REVERSE
+) {
+    startAnimation(AlphaAnimation(minAlpha, maxAlpha).also {
+        it.duration = duration
+        it.startOffset = offset
+        it.repeatMode = repeatMode
+        it.repeatCount = times
+    })
+
+
+}
+
+
+fun View.zoomIn(): Completable {
+    return Completable.create {
+        //alpha = 0f
+        //pivotX = 0.5f
+       // pivotY = 0.5f
+        scaleX = 4f
+        scaleY = 4f
+        //translationY = 0f
+        //translationX = 0f
+        animate().alpha(1f)
+            //.translationY(0f)
+            //.translationX(0f)
+            .scaleX(1f)
+            .scaleY(1f)
+            .setDuration(200)
+            .setInterpolator(LinearInterpolator())
+            .withEndAction(it::onComplete)
+    }
+}
+fun View.waitingLotto(): Completable {
+    return Completable.create {
+//        alpha = 0f
+//        scaleX = 0.5f
+//        scaleY = 0f
+//        translationY = 0f
+//        translationX = 20f
+//        animate().alpha(1f)
+//            .translationY(0f)
+//            .translationX(0f)
+//            .scaleX(1f)
+//            .scaleY(1f)
+//            .setDuration(200)
+//            .setInterpolator(OvershootInterpolator())
+//            .withEndAction(it::onComplete)
+        alpha = 0f
+        animate().alpha(1f)
+            .setDuration(50)
+            .withEndAction(it::onComplete)
+    }
+}
+fun View.btnClick(): Completable {
+    return Completable.create {
+        //visibility = View.VISIBLE
+        alpha = 0f
+        animate().alpha(1f)
+            .setDuration(30)
+            .withEndAction(it::onComplete)
+    }
+}
+fun View.resultBlink(): Completable {
+    return Completable.create {
+        //visibility = View.VISIBLE
+        alpha = 0f
+        animate().alpha(1f)
+            .setDuration(200)
+            .withEndAction(it::onComplete)
+    }
+}
+fun View.randomResult(): Completable {
+    return Completable.create {
+        //visibility = View.VISIBLE
+        alpha = 0f
+        animate().alpha(1f)
+//            .also {
+//                val rdns = (1..80).random()
+//                tvNumber.text= "$rdns"
+//            }
+            .setDuration(5)
+            .withEndAction(it::onComplete)
+    }
+}
+fun AnimationDrawable.onAnimationFinished(block: () -> Unit) {
+    var duration: Long = 0
+    for (i in 0..numberOfFrames) {
+        duration += getDuration(i)
+    }
+    Handler().postDelayed({
+        block()
+    }, duration*5)
+}
+fun IntRange.random() = Random().nextInt((endInclusive + 1) - start) + start
+
+fun View.setSafeOnClickListener(onSafeClick: (View) -> Unit) {
+    val safeClickListener = SafeClickListener {
+        onSafeClick(it)
+    }
+    setOnClickListener(safeClickListener)
+}
+
+fun <T> MutableList<T>.prepend(element: T) {
+    add(0, element)
+}

+ 55 - 0
app/src/main/java/com/khmer9/yuki/remote/CreateBettingDataModel.kt

@@ -0,0 +1,55 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class CreateBettingRequest(
+    @Json(name = "yuki_id")
+    val lottoId: String,
+
+    @Json(name = "amount")
+    val amount: Int
+)
+
+@JsonClass(generateAdapter = true)
+data class CreateBettingResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: CreateBettingMessage,
+
+    @Json(name = "data")
+    val data: CreateBettingData?
+)
+
+@JsonClass(generateAdapter = true)
+data class CreateBettingMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class CreateBettingData(
+    @Json(name = "invoice_no")
+    val id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "bet_types")
+    val betTypes: List<BetType>,
+
+    @Json(name = "total_amount")
+    val amount: String,
+
+    @Json(name = "time")
+    val time: String,
+
+    @Json(name = "balance")
+    val balance: String
+)

+ 67 - 0
app/src/main/java/com/khmer9/yuki/remote/CurrentBettingDataModel.kt

@@ -0,0 +1,67 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: CurrentBettingMessage,
+
+    @Json(name = "data")
+    val data: CurrentBettingData?
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingData(
+    @Json(name = "balance")
+    val balance: String,
+
+    @Json(name = "total_amount")
+    val totalAmount: Int,
+
+    @Json(name = "count")
+    val count: Int,
+
+    @Json(name = "list")
+    val currentBettingList: List<CurrentBettingList>?
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingList(
+    @Json(name = "_id")
+    val id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "betting")
+    val betting: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+
+    @Json(name = "bet_color")
+    val betColor: String,
+
+    @Json(name = "amount")
+    val amount: String,
+
+    @Json(name = "time")
+    val time: String
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}

+ 79 - 0
app/src/main/java/com/khmer9/yuki/remote/CurrentBettingMobileDataModel.kt

@@ -0,0 +1,79 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingMobileResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: CurrentBettingMobileMessage,
+
+    @Json(name = "data")
+    val data: CurrentBettingMobileData?
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingMobileMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingMobileData(
+    @Json(name = "balance")
+    val balance: Double,
+
+    @Json(name = "list")
+    val list: List<CurrentBettingMobile>?
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBettingMobile(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "total_amount")
+    val totalAmount: Double,
+
+    @Json(name = "is_release_result")
+    val isReleaseResult: Boolean,
+
+    @Json(name = "total_amount_win")
+    val totalAmountWin: Double? = 0.0,
+
+    @Json(name = "total_cast")
+    val totalCast: Double? = 0.0,
+
+    @Json(name = "time")
+    val time: String,
+
+    @Json(name = "bet_type")
+    val betType: List<BetType>?
+
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class BetType(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "amount")
+    var amount: Int,
+
+    @Json(name = "name")
+    var name: String
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}

+ 34 - 0
app/src/main/java/com/khmer9/yuki/remote/CurrentGameDataModel.kt

@@ -0,0 +1,34 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class CurrentGameResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: CurrentGameMessage,
+
+    @Json(name = "data")
+    val data: CurrentGameData?
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentGameMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentGameData(
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "date")
+    val date: String,
+)

+ 82 - 0
app/src/main/java/com/khmer9/yuki/remote/LoginDataModel.kt

@@ -0,0 +1,82 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class LoginRequest(
+    @Json(name = "user_name")
+    val username: String,
+
+    @Json(name = "password")
+    val password: String,
+
+    @Json(name = "app_id")
+    val appId: String? = "630475c2eca0ac8bd61e7e58"
+)
+
+@JsonClass(generateAdapter = true)
+data class LoginResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: LoginMessage,
+
+    @Json(name = "data")
+    val data: LoginData?
+)
+
+@JsonClass(generateAdapter = true)
+data class LoginMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class LoginData(
+    @Json(name = "user")
+    val user: User?,
+
+    @Json(name = "token")
+    val token: String?,
+)
+
+@JsonClass(generateAdapter = true)
+data class User(
+    @Json(name = "user_name")
+    val userName: String,
+
+    @Json(name = "role")
+    val role: String,
+
+    @Json(name = "enable")
+    val enable: Boolean,
+
+    @Json(name = "is_suspend")
+    val isSuspend: Boolean,
+
+    @Json(name = "account_info")
+    val accountInfo: String,
+
+    @Json(name = "account_name")
+    val accountName: String?,
+
+    @Json(name = "account_number")
+    val accountNumber: String?,
+
+    @Json(name = "balance")
+    val accountBalances: Double,
+
+    @Json(name = "currency_type")
+    val currencyType: Int,
+
+    @Json(name = "amount_min")
+    val amountMin: Int? = 0,
+
+    @Json(name = "amount_max")
+    val amountMax: Int? = 0,
+)

+ 47 - 0
app/src/main/java/com/khmer9/yuki/remote/LottoSetDataModel.kt

@@ -0,0 +1,47 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class LottoSetResponse(
+    @Json(name = "code")
+    val resultCode: String,
+    @Json(name = "message")
+    val message: ReportLottoSetMessage,
+    @Json(name = "data")
+    val data: List<LottoSet>?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportLottoSetMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class LottoSet(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+
+    @Json(name = "name")
+    val typeOfBetting: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "payout")
+    val payment: Double,
+
+    @Json(name = "type")
+    val type: Int,
+
+    @Json(name = "is_click")
+    var isClick: Boolean = false
+)

+ 117 - 0
app/src/main/java/com/khmer9/yuki/remote/MainDataModel.kt

@@ -0,0 +1,117 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class MainResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: MainMessage,
+
+    @Json(name = "data")
+    val data: MainData?
+)
+
+@JsonClass(generateAdapter = true)
+data class MainMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class MainData(
+    @Json(name = "accountBalances")
+    val accountBalance: String,
+
+    @Json(name = "newResult")
+    val nextNo: String?,
+
+    @Json(name = "currentResult")
+    val result: Result?,
+
+    @Json(name = "currentBetting")
+    val myBetting: MyBetting?
+)
+
+@JsonClass(generateAdapter = true)
+data class MyBetting(
+    @Json(name = "_id")
+    var id: String?,
+
+    @Json(name = "data")
+    val data: List<ListItem>?
+)
+
+@JsonClass(generateAdapter = true)
+data class ListItem(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "set_result_id")
+    var setId: String,
+
+    @Json(name = "type_of_betting")
+    val typeOfBetting: String,
+
+    @Json(name = "category")
+    val category: String,
+
+    @Json(name = "user_id")
+    val user_id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "amount")
+    val amount: String,
+
+    @Json(name = "cast")
+    val cast: String,
+
+    @Json(name = "win_lose")
+    val win_lose: String?,
+
+    @Json(name = "amount_win")
+    val amount_win: String?
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class Result(
+
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "no")
+    var no: String,
+
+    @Json(name = "total_ramdom")
+    var totalValue: String,
+
+    @Json(name = "set_result_id")
+    var smallBigSetId: String,
+
+    @Json(name = "type_of_betting")
+    val resultSmallOrBig: String,
+
+    @Json(name = "category")
+    var categorySmallOrBig: String,
+
+    @Json(name = "set_result_id_range")
+    var rangeSetId: String,
+
+    @Json(name = "type_of_betting_range")
+    val resultRange: String,
+
+    @Json(name = "category_range")
+    var categoryRange: String
+)
+

+ 93 - 0
app/src/main/java/com/khmer9/yuki/remote/PlayDataModel.kt

@@ -0,0 +1,93 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class PlayRequest(
+    @Json(name = "set_result_id")
+    val setID: String,
+
+    @Json(name = "user_id")
+    val userId: String,
+
+    @Json(name = "no")
+    val no: String,
+
+    @Json(name = "cast")
+    val cast: Double,
+
+    @Json(name = "amount")
+    val amount: Int
+)
+
+@JsonClass(generateAdapter = true)
+data class PlayResponse(
+
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: PlayMessage,
+
+    @Json(name = "data")
+    val data: PlayData?
+)
+
+@JsonClass(generateAdapter = true)
+data class PlayMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class PlayData(
+
+    @Json(name = "objCurrentBetting")
+    val myBetting: CurrentBetting?,
+
+    @Json(name = "accountBalances")
+    val accountBalance: String?,
+)
+
+@JsonClass(generateAdapter = true)
+data class CurrentBetting(
+
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "no")
+    var no: String,
+
+    @Json(name = "set_result_id")
+    var setId: String,
+
+    @Json(name = "type_of_betting")
+    val typeOfBetting: String,
+
+    @Json(name = "category")
+    val category: String,
+
+    @Json(name = "user_id")
+    val userId: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "amount")
+    val amount: String,
+
+    @Json(name = "cast")
+    val cast: String,
+
+    @Json(name = "win_lose")
+    val win_lose: String?,
+
+    @Json(name = "amount_win")
+    val amount_win: String?
+
+)
+

+ 70 - 0
app/src/main/java/com/khmer9/yuki/remote/PreviousBettingDataModel.kt

@@ -0,0 +1,70 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class PreviousBettingResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: PreviousBettingMessage,
+
+    @Json(name = "data")
+    val data: PreviousBettingData?
+)
+
+@JsonClass(generateAdapter = true)
+data class PreviousBettingMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class PreviousBettingData(
+    @Json(name = "one")
+    val one: List<PreviousBettingList>?,
+
+    @Json(name = "two")
+    val two: List<PreviousBettingList>?,
+
+    @Json(name = "three")
+    val three: List<PreviousBettingList>?,
+
+    @Json(name = "four")
+    val four: List<PreviousBettingList>?,
+
+    @Json(name = "five")
+    val five: List<PreviousBettingList>?
+)
+
+@JsonClass(generateAdapter = true)
+data class PreviousBettingList(
+    @Json(name = "_id")
+    val id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "betting")
+    val betting: String,
+
+    @Json(name = "bet_color")
+    val betColor: String,
+
+    @Json(name = "is_win")
+    val isWin: Boolean,
+
+    @Json(name = "amount")
+    val amount: String,
+
+    @Json(name = "time")
+    val time: String
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}

+ 99 - 0
app/src/main/java/com/khmer9/yuki/remote/ReportDataBettingModel.kt

@@ -0,0 +1,99 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class ReportBettingRequest(
+    @Json(name = "start_date")
+    val startDate: String,
+
+    @Json(name = "end_date")
+    val endDate: String,
+
+    @Json(name = "rows_per_page")
+    val rowPerPage: Int,
+
+    @Json(name = "page")
+    val page: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportBettingResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ReportBettingMessage,
+
+    @Json(name = "data")
+    val data: ReportBettingData?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportBettingMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportBettingData(
+    @Json(name = "turn_over")
+    val turnOver: String,
+
+    @Json(name = "amount_win_lose")
+    val amountWinLose: String,
+
+    @Json(name = "is_win")
+    val isWin: Boolean,
+
+    @Json(name = "reports")
+    val statementList: List<StatementBetting>?
+)
+
+//-------------------------------Report Betting Statement
+
+@JsonClass(generateAdapter = true)
+data class StatementBetting(
+    @Json(name = "_id")
+    val id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "time")
+    val date: String,
+
+    @Json(name = "total_amount")
+    val amount: Double,
+
+    @Json(name = "total_cast")
+    val cast: Double,
+
+    @Json(name = "total_amount_win")
+    val winLose: Double,
+
+    @Json(name = "bet_type")
+    val bet_type: List<BettingType>,
+
+    ) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class BettingType(
+    @Json(name = "_id")
+    val id: String,
+
+    @Json(name = "name")
+    val name: String,
+
+    @Json(name = "amount")
+    val amount: Double
+)
+
+//-------------------------------//End Report Betting Statement

+ 197 - 0
app/src/main/java/com/khmer9/yuki/remote/ReportDataModel.kt

@@ -0,0 +1,197 @@
+package com.khmer9.yuki.remote
+
+/*
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class ReportResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ReportMessage,
+
+    @Json(name = "data")
+    val data: ReportData?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportData(
+    @Json(name = "objLotoBettingWin")
+    val winList: List<Win>?,
+
+    @Json(name = "objLotoBettingWinLose")
+    val winLostLst: List<WinLost>?,
+
+    @Json(name = "reports")
+    val statementList: List<Statement>?
+)
+
+//-------------------------------Report Betting Statement
+
+@JsonClass(generateAdapter = true)
+data class Statement(
+    @Json(name = "no")
+    var id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "amount")
+    val amount: Double,
+
+    @Json(name = "bet_type")
+    val bet_type: BetType,
+
+    @Json(name = "amount_win")
+    val win_lose: Double
+
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class BetType(
+    @Json(name = "name")
+    val type: String,
+
+    @Json(name = "payout")
+    val payout: Double
+)
+
+//-------------------------------//End Report Betting Statement
+
+//-------------------------------Report Withdraw Statement
+
+@JsonClass(generateAdapter = true)
+data class WithdrawStatement(
+    @Json(name = "no")
+    var id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "amount")
+    val amount: Double,
+
+    @Json(name = "old_balance")
+    val oldBalance: String,
+
+    @Json(name = "new_balance")
+    var newBalance: Double
+
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+//-------------------------------//End Report Withdraw Statement
+
+*/
+/*@Json(name = "note")
+    val note: String,
+
+    @Json(name = "note_khmer")
+    val khmerNote: String?,*//*
+
+
+//------------------------------------------ unused data
+
+@JsonClass(generateAdapter = true)
+data class Win(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "filter_date")
+    var filterDate: String,
+
+    @Json(name = "no")
+    val no: String,
+
+    @Json(name = "user_name")
+    val userName: String,
+
+    @Json(name = "bank_account")
+    val bankAccount: String,
+
+    @Json(name = "total_ramdom")
+    val totalRandom: String,
+
+    @Json(name = "type_of_betting")
+    val typeOfBetting: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "amount")
+    val amount: String,
+
+    @Json(name = "cast")
+    val cast: String,
+
+    @Json(name = "win_lose")
+    val win_lose: String?,
+
+    @Json(name = "amount_win")
+    val amount_win: String?
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class WinLost(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "filter_date")
+    var filterDate: String,
+
+    @Json(name = "no")
+    val no: String,
+
+    @Json(name = "user_name")
+    val userName: String,
+
+    @Json(name = "bank_account")
+    val bankAccount: String,
+
+    @Json(name = "total_ramdom")
+    val totalRandom: String,
+
+    @Json(name = "type_of_betting")
+    val typeOfBetting: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "amount")
+    val amount: String,
+
+    @Json(name = "cast")
+    val cast: String,
+
+    @Json(name = "win_lose")
+    val win_lose: String?,
+
+    @Json(name = "amount_win")
+    val amount_win: String?
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+*/

+ 155 - 0
app/src/main/java/com/khmer9/yuki/remote/ReportDataResultModel.kt

@@ -0,0 +1,155 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class ReportResultRequest(
+    @Json(name = "start_date")
+    val startDate: String,
+
+    @Json(name = "end_date")
+    val endDate: String,
+
+    @Json(name = "rows_per_page")
+    val rowPerPage: Int,
+
+    @Json(name = "page")
+    val page: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportResultResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ReportResultMessage,
+
+    @Json(name = "data")
+    val data: ReportResultData?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportResultMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportResultData(
+    @Json(name = "results")
+    val resultList: List<StatementResult>?
+)
+
+@JsonClass(generateAdapter = true)
+data class StatementResult(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "small_large")
+    val smallLarge: ResultSmallLarge,
+
+    @Json(name = "bonus")
+    val resultBonus: ResultBonus,
+
+    @Json(name = "range")
+    val resultRange: ResultRange,
+
+    @Json(name = "blue_red")
+    val resultBlueRed: ResultBlueRed,
+
+    @Json(name = "odd_even")
+    val resultOddEven: ResultOddEven,
+
+    @Json(name = "year")
+    val resultYear: ResultYear,
+
+    ) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class ResultSmallLarge(
+    @Json(name = "name")
+    val name: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ResultBonus(
+    @Json(name = "year_number")
+    val yearNumber: Int,
+
+    @Json(name = "is_bonus")
+    val isBonus: Boolean,
+
+    @Json(name = "payout")
+    val payout: Int
+)
+
+@JsonClass(generateAdapter = true)
+data class ResultRange(
+    @Json(name = "name")
+    val name: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ResultBlueRed(
+    @Json(name = "name")
+    val name: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ResultOddEven(
+    @Json(name = "name")
+    val name: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ResultYear(
+    @Json(name = "name")
+    val name: String,
+
+    @Json(name = "color")
+    val color: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+
+

+ 73 - 0
app/src/main/java/com/khmer9/yuki/remote/ReportDataWithdrawModel.kt

@@ -0,0 +1,73 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class ReportWithdrawRequest(
+    @Json(name = "start_date")
+    val startDate: String,
+
+    @Json(name = "end_date")
+    val endDate: String,
+
+    @Json(name = "rows_per_page")
+    val rowPerPage: Int,
+
+    @Json(name = "page")
+    val page: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportWithdrawResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ReportWithdrawMessage,
+
+    @Json(name = "data")
+    val data: ReportWithdrawData?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportWithdrawMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportWithdrawData(
+    @Json(name = "total_amount")
+    val totalAmount: String?,
+
+    @Json(name = "reports")
+    val statementWithdrawList: List<StatementWithdraw>?
+)
+
+//-------------------------------Report Withdraw Statement
+
+@JsonClass(generateAdapter = true)
+data class StatementWithdraw(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "amount")
+    val amount: Double,
+
+    @Json(name = "old_balance")
+    val oldBalance: Double,
+
+    @Json(name = "new_balance")
+    val newBalance: Double
+
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}

+ 73 - 0
app/src/main/java/com/khmer9/yuki/remote/ReportDepositDataModel.kt

@@ -0,0 +1,73 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class ReportDepositRequest(
+    @Json(name = "start_date")
+    val startDate: String,
+
+    @Json(name = "end_date")
+    val endDate: String,
+
+    @Json(name = "rows_per_page")
+    val rowPerPage: Int,
+
+    @Json(name = "page")
+    val page: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportDepositResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ReportDepositMessage,
+
+    @Json(name = "data")
+    val data: ReportDepositData?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportDepositMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportDepositData(
+    @Json(name = "total_amount")
+    val totalAmount: String?,
+
+    @Json(name = "reports")
+    val statementDepositList: List<StatementDeposit>?
+)
+
+//-------------------------------Report Withdraw Statement
+
+@JsonClass(generateAdapter = true)
+data class StatementDeposit(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "date")
+    val date: String,
+
+    @Json(name = "amount")
+    val amount: Double,
+
+    @Json(name = "old_balance")
+    val oldBalance: Double,
+
+    @Json(name = "new_balance")
+    val newBalance: Double
+
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}

+ 150 - 0
app/src/main/java/com/khmer9/yuki/remote/ResultDataModel.kt

@@ -0,0 +1,150 @@
+package com.khmer9.yuki.remote
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class ResultResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ResultMessage,
+
+    @Json(name = "data")
+    val data: List<ResultLotto>?
+)
+
+@JsonClass(generateAdapter = true)
+data class ResultMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+//----------------------------------------------------
+
+@JsonClass(generateAdapter = true)
+data class ResultLotto(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "no")
+    var no: Int,
+
+    @Json(name = "date")
+    var date: String,
+
+    @Json(name = "small_large")
+    var smallLarge: SmallLarge,
+
+    @Json(name = "bonus")
+    var bonus: Bonus,
+
+    @Json(name = "range")
+    var range: Range,
+
+    @Json(name = "blue_red")
+    var blueRed: BlueRed,
+
+    @Json(name = "odd_even")
+    var oddEven: OddEven,
+
+    @Json(name = "year")
+    var year: Year
+
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class SmallLarge(
+    @Json(name = "name")
+    var name: String,
+
+    @Json(name = "color")
+    var color: String,
+
+    @Json(name = "year_number")
+    var yearNumber: Int
+
+) : BaseListItem() {
+    override fun getUnique(): String = name
+}
+
+@JsonClass(generateAdapter = true)
+data class Bonus(
+    @Json(name = "year_number")
+    var name: String,
+
+    @Json(name = "is_bonus")
+    var isBonus: Boolean,
+
+    @Json(name = "payout")
+    var payout: Double,
+) : BaseListItem() {
+    override fun getUnique(): String = name
+}
+
+@JsonClass(generateAdapter = true)
+data class Range(
+    @Json(name = "name")
+    var name: String,
+
+    @Json(name = "color")
+    var color: String,
+
+    @Json(name = "year_number")
+    var yearNumber: Int
+
+) : BaseListItem() {
+    override fun getUnique(): String = name
+}
+
+@JsonClass(generateAdapter = true)
+data class BlueRed(
+    @Json(name = "name")
+    var name: String,
+
+    @Json(name = "color")
+    var color: String,
+
+    @Json(name = "year_number")
+    var yearNumber: Int
+
+) : BaseListItem() {
+    override fun getUnique(): String = name
+}
+
+@JsonClass(generateAdapter = true)
+data class OddEven(
+    @Json(name = "name")
+    var name: String,
+
+    @Json(name = "color")
+    var color: String,
+
+    @Json(name = "year_number")
+    var yearNumber: Int
+
+) : BaseListItem() {
+    override fun getUnique(): String = name
+}
+
+@JsonClass(generateAdapter = true)
+data class Year(
+    @Json(name = "name")
+    var name: String,
+
+    @Json(name = "color")
+    var color: String,
+
+    @Json(name = "year_number")
+    var yearNumber: Int
+
+) : BaseListItem() {
+    override fun getUnique(): String = name
+}

+ 16 - 0
app/src/main/java/com/khmer9/yuki/remote/SocketMessageDataModel.kt

@@ -0,0 +1,16 @@
+package com.khmer9.yuki.remote
+
+import org.json.JSONArray
+
+data class SocketMessageDataModel(
+    val type: String,
+    val countDown: Int? = null,
+    val resultArray: JSONArray? = null,
+    val totalValue: Int? = null,
+    val isBonus: Boolean? = false,
+    val bonusNumber: Int? = null,
+    val bonusValue: Int? = null
+
+) {
+    fun isCountDowning() = countDown != null && resultArray == null
+}

+ 30 - 0
app/src/main/java/com/khmer9/yuki/remote/UpdatePwdDataModel.kt

@@ -0,0 +1,30 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+class UpdatePwdRequest(
+    @Json(name = "user_name")
+    val username: String,
+    @Json(name = "password")
+    val password: String
+)
+@JsonClass(generateAdapter = true)
+data class UpdatePwdResponse(
+
+    @Json(name = "code")
+    val resultCode: String,
+    @Json(name = "message")
+    val message: LoginMessage,
+
+)
+@JsonClass(generateAdapter = true)
+data class UpdatePwdMessage(
+    @Json(name="code")
+    val code: String,
+
+    @Json(name="descriptions")
+    val description: String,
+)
+

+ 46 - 0
app/src/main/java/com/khmer9/yuki/remote/YukiSetDataModel.kt

@@ -0,0 +1,46 @@
+package com.khmer9.yuki.remote
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class YukiSetResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: ReportYukiSetMessage,
+
+    @Json(name = "data")
+    val data: List<YukiSet>?
+)
+
+@JsonClass(generateAdapter = true)
+data class ReportYukiSetMessage(
+    @Json(name = "code")
+    val code: String,
+
+    @Json(name = "descriptions")
+    val description: String
+)
+
+@JsonClass(generateAdapter = true)
+data class YukiSet(
+    @Json(name = "_id")
+    var id: String,
+
+    @Json(name = "year_number")
+    val year_number: Int,
+
+    @Json(name = "name")
+    val name: Int,
+
+    @Json(name = "color")
+    val color: Int,
+
+    @Json(name = "payout")
+    val payout: Double,
+
+    @Json(name = "type")
+    val type: Int
+)

+ 13 - 0
app/src/main/java/com/khmer9/yuki/remote/main/SocketClientNewGameDataModel.kt

@@ -0,0 +1,13 @@
+package com.khmer9.yuki.remote.main
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class SocketClientNewGameDataModel(
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "date")
+    val date: String
+)

+ 13 - 0
app/src/main/java/com/khmer9/yuki/remote/main/SocketClientRandomDataModel.kt

@@ -0,0 +1,13 @@
+package com.khmer9.yuki.remote.main
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class SocketClientRandomDataModel(
+    @Json(name = "randmon_time")
+    val randomTime: Int? = null,
+
+    @Json(name = "is_open")
+    val isOpen: Boolean
+)

+ 134 - 0
app/src/main/java/com/khmer9/yuki/remote/main/SocketClientResultDataModel.kt

@@ -0,0 +1,134 @@
+package com.khmer9.yuki.remote.main
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.json.JSONArray
+
+@JsonClass(generateAdapter = true)
+data class SocketClientResultDataModel(
+    @Json(name = "_id")
+    val id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "bonus_between_year")
+    val bonusBetweenYear: BonusBetweenYear,
+
+    @Json(name = "between_year")
+    val betweenYear: BetweenYear,
+
+    @Json(name = "small_large")
+    val smallLarge: SmallLarge,
+
+    @Json(name = "range")
+    val range: Range,
+
+    @Json(name = "blue_red")
+    val blueRed: BlueRed,
+
+    @Json(name = "odd_even")
+    val oddEven: OddEven,
+)
+
+@JsonClass(generateAdapter = true)
+data class BonusBetweenYear(
+    @Json(name = "is_bonus")
+    val isBonus: Boolean,
+
+    @Json(name = "payout")
+    val payout: Double,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class BetweenYear(
+    @Json(name = "year_name_kh")
+    val yearNameKh: String,
+
+    @Json(name = "year_name_vn")
+    val yearNameVn: String,
+
+    @Json(name = "year_name_tb")
+    val yearNameTb: String,
+
+    @Json(name = "year_name_en")
+    val yearNameEn: String,
+
+    @Json(name = "year_number")
+    val yearNumber: Int,
+)
+
+@JsonClass(generateAdapter = true)
+data class SmallLarge(
+    @Json(name = "year_name_kh")
+    val yearNameKh: String,
+
+    @Json(name = "year_name_vn")
+    val yearNameVn: String,
+
+    @Json(name = "year_name_tb")
+    val yearNameTb: String,
+
+    @Json(name = "year_name_en")
+    val yearNameEn: String,
+
+    @Json(name = "year_number")
+    val yearNumber: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class Range(
+    @Json(name = "year_name_kh")
+    val yearNameKh: String,
+
+    @Json(name = "year_name_vn")
+    val yearNameVn: String,
+
+    @Json(name = "year_name_tb")
+    val yearNameTb: String,
+
+    @Json(name = "year_name_en")
+    val yearNameEn: String,
+
+    @Json(name = "year_number")
+    val yearNumber: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class BlueRed(
+    @Json(name = "year_name_kh")
+    val yearNameKh: String,
+
+    @Json(name = "year_name_vn")
+    val yearNameVn: String,
+
+    @Json(name = "year_name_tb")
+    val yearNameTb: String,
+
+    @Json(name = "year_name_en")
+    val yearNameEn: String,
+
+    @Json(name = "year_number")
+    val yearNumber: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class OddEven(
+    @Json(name = "year_name_kh")
+    val yearNameKh: String,
+
+    @Json(name = "year_name_vn")
+    val yearNameVn: String,
+
+    @Json(name = "year_name_tb")
+    val yearNameTb: String,
+
+    @Json(name = "year_name_en")
+    val yearNameEn: String,
+
+    @Json(name = "year_number")
+    val yearNumber: String,
+)

+ 19 - 0
app/src/main/java/com/khmer9/yuki/remote/main/SocketClientTimeDataModel.kt

@@ -0,0 +1,19 @@
+package com.khmer9.yuki.remote.main
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.json.JSONArray
+
+@JsonClass(generateAdapter = true)
+data class SocketClientTimeDataModel(
+    @Json(name = "time")
+    val time: Int,
+
+    @Json(name = "is_open_betting")
+    val isOpenBetting: Boolean,
+
+    @Json(name = "is_countdown")
+    val isCountdown: Boolean
+) {
+    fun isCountDowning() = time != 0
+}

+ 56 - 0
app/src/main/java/com/khmer9/yuki/remote/service/ApiService.kt

@@ -0,0 +1,56 @@
+package com.khmer9.yuki.remote.service
+
+import com.khmer9.yuki.remote.*
+import com.khmer9.yuki.screen.detail.BettingDetailResponse
+import io.reactivex.Single
+import retrofit2.http.*
+
+interface ApiService {
+    @POST("/v1/api/user/login")
+    @Headers("No-Authentication: true")
+    fun login(@Body loginRequest: LoginRequest): Single<LoginResponse>
+
+    //Report api
+
+    @POST("/v1/api/report/betting")
+    fun remoteBettingScreen(@Body reportBettingRequest: ReportBettingRequest? = null): Single<ReportBettingResponse>
+
+    @POST("/v1/api/report/withdraw")
+    fun remoteWithdrawScreen(@Body reportWithdrawRequest: ReportWithdrawRequest? = null): Single<ReportWithdrawResponse>
+
+    @POST("/v1/api/report/deposit")
+    fun remoteDepositScreen(@Body reportDepositRequest: ReportDepositRequest? = null): Single<ReportDepositResponse>
+
+    @POST("/v1/api/result/filter")
+    fun remoteResultStatementScreen(@Body reportResultRequest: ReportResultRequest? = null): Single<ReportResultResponse>
+
+    //Home api
+    //TODO: Change <LottoSetResponse> to <YukiSetResponse>
+    @GET("/v1/api/type")
+    fun remoteLottoSet(): Single<LottoSetResponse>
+
+    @GET("/v1/api/betting/previous")
+    fun remotePreviousBetting(): Single<PreviousBettingResponse>
+
+    @GET("/v1/api/result")
+    fun remoteResultScreen(): Single<ResultResponse>
+
+    @GET("/v1/api/betting/current")
+    fun remoteCurrentBetting(): Single<CurrentBettingResponse>
+
+    @POST("/v1/api/betting/")
+    fun createBetting(@Body bettingRequest: List<CreateBettingRequest>): Single<CreateBettingResponse>
+
+    @GET("/v1/api/game")
+    fun remoteCurrentGame(): Single<CurrentGameResponse>
+
+    @GET("/v1/api/betting/currentMobile")
+    fun remoteCurrentBettingMobile(): Single<CurrentBettingMobileResponse>
+
+    @FormUrlEncoded
+    @POST("/v1/api/user/changepassword")
+    fun updatePassword(@Field(value = "old_password") userName: String, @Field(value = "new_password") password: String): Single<UpdatePwdResponse>
+
+    @GET("/v1/api/betting/detail/")
+    fun getBettingDetail(@Query("invoice_no") invoiceNum: String): Single<BettingDetailResponse>
+}

+ 19 - 0
app/src/main/java/com/khmer9/yuki/remote/service/ConnectivityStates.java

@@ -0,0 +1,19 @@
+package com.khmer9.yuki.remote.service;
+
+public class ConnectivityStates {
+
+    private ConnectivityStates() {
+        // non-instantiable class
+    }
+
+    public static final int STATE_NOT_CONNECTED = 0 ;
+
+    public static final int STATE_RECONNECTING = 1 ;
+
+    public static final int STATE_CONNECTED = 2 ;
+
+    public static final int STATE_DISCONNECTED = 3 ;
+
+    public static final int STATE_TIMEOUT = 4 ;
+
+}

+ 25 - 0
app/src/main/java/com/khmer9/yuki/remote/service/EventListener.java

@@ -0,0 +1,25 @@
+package com.khmer9.yuki.remote.service;
+
+public interface EventListener {
+    void onConnect();
+
+    void onConnectionError();
+
+    void onDisconnect();
+
+    void onClientNewGameListener(String data);
+
+    void onClientRandomListener(String data);
+
+    void onClientResetListener(String data);
+
+    void onClientResultListener(String data);
+
+    void onClientUpdateInfoListener(String data);
+
+    void onClientTimeListener(String data);
+
+    void onReconnect();
+
+    void onReconnectionTimeout();
+}

+ 130 - 0
app/src/main/java/com/khmer9/yuki/remote/service/SocketService.java

@@ -0,0 +1,130 @@
+package com.khmer9.yuki.remote.service;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+
+import android.util.Log;
+
+import com.khmer9.yuki.config.Config;
+import com.khmer9.yuki.util.BaseObservable;
+
+import org.json.JSONObject;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import io.socket.client.IO;
+import io.socket.client.Socket;
+import io.socket.emitter.Emitter;
+
+public class SocketService extends BaseObservable<EventListener> {
+
+    private static final String TAG = "Socket";
+
+    private static final String SOCKET_URL = Config.SOCKET_URL;
+
+    private static final String EVENT_CONNECT = Socket.EVENT_CONNECT;
+    private static final String EVENT_CONNECT_ERROR = Socket.EVENT_CONNECT_ERROR;
+    private static final String EVENT_DISCONNECT = Socket.EVENT_DISCONNECT;
+
+    private static final String EVENT_CLIENT_NEW_GAME = "client-new-game";
+    private static final String EVENT_CLIENT_RANDOM = "client-random";
+    private static final String EVENT_CLIENT_RESET = "client-reset";
+    private static final String EVENT_CLIENT_RESULT = "client-result";
+    private static final String EVENT_CLIENT_UPDATE_INFO = "client-update-info";
+    private static final String EVENT_CLIENT_TIME = "client-time";
+
+    private Socket socket;
+
+    public void startListening(String token) throws URISyntaxException {
+        IO.Options options = IO.Options.builder()
+                .setExtraHeaders(singletonMap("Authorization", singletonList("Bearer " + token)))
+                .build();
+
+        if (socket == null) {
+            socket = IO.socket(URI.create(SOCKET_URL), options);
+        }
+
+        socket.on(EVENT_CONNECT, connectListener);
+        socket.on(EVENT_CLIENT_NEW_GAME, clientNewGameListener);
+        socket.on(EVENT_CLIENT_RANDOM, clientRandomListener);
+        socket.on(EVENT_CLIENT_RESET, clientResetListener);
+        socket.on(EVENT_CLIENT_RESULT, clientResultListener);
+        socket.on(EVENT_CLIENT_UPDATE_INFO, clientUpdateInfoListener);
+        socket.on(EVENT_CLIENT_TIME, clientTimeListener);
+        socket.on(EVENT_DISCONNECT, disconnectListener);
+        socket.on(EVENT_CONNECT_ERROR, connectionErrorListener);
+        socket.connect();
+    }
+
+    private Emitter.Listener clientNewGameListener = args -> {
+        Log.d("Socket", "clientNewGameListener s...");
+        final String rawMessage = args[0].toString();
+        for (EventListener listener : getListeners())
+            listener.onClientNewGameListener(rawMessage);
+    };
+
+    private Emitter.Listener clientRandomListener = args -> {
+        Log.d("Socket", "clientRandomListener s...");
+        final String rawMessage = args[0].toString();
+        for (EventListener listener : getListeners())
+            listener.onClientRandomListener(rawMessage);
+    };
+
+    private Emitter.Listener clientResetListener = args -> {
+        Log.d("Socket", "clientResetListener s...");
+        final String rawMessage = args[0].toString();
+        for (EventListener listener : getListeners())
+            listener.onClientResetListener(rawMessage);
+    };
+
+    private Emitter.Listener clientResultListener = args -> {
+        Log.d("Socket", "clientResultListener s...");
+        final String rawMessage = args[0].toString();
+        for (EventListener listener : getListeners())
+            listener.onClientResultListener(rawMessage);
+    };
+
+    private Emitter.Listener clientUpdateInfoListener = args -> {
+        Log.d("Socket", "clientUpdateInfoListener s...");
+        final String rawMessage = args[0].toString();
+        for (EventListener listener : getListeners())
+            listener.onClientUpdateInfoListener(rawMessage);
+    };
+
+    private Emitter.Listener clientTimeListener = args -> {
+        Log.d("Socket", "clientTimeListener s...");
+        final String rawMessage = args[0].toString();
+        for (EventListener listener : getListeners())
+            listener.onClientTimeListener(rawMessage);
+    };
+
+    private Emitter.Listener connectListener = args -> {
+        Log.d("Socket", "onConnect s...");
+        for (EventListener listener : getListeners())
+            listener.onConnect();
+    };
+
+    private Emitter.Listener connectionErrorListener = args -> {
+        Log.d(TAG, "onConnectionError s..." + args[0].toString());
+        for (EventListener listener : getListeners())
+            listener.onConnectionError();
+    };
+
+    private Emitter.Listener disconnectListener = args -> {
+        Log.d(TAG, "onDisconnect s..." + args[0].toString());
+        socket.off();
+        for (EventListener listener : getListeners()) {
+            listener.onDisconnect();
+        }
+    };
+
+    public void startEmitCfCreateBetting(JSONObject joinObject) {
+        socket.emit("player-betting", joinObject);
+    }
+
+    public void stopListening() {
+        if (socket != null)
+            socket.disconnect();
+    }
+}

+ 19 - 0
app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailAdapter.kt

@@ -0,0 +1,19 @@
+package com.khmer9.yuki.screen.detail
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+
+class BetsDetailAdapter : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String, String) -> Unit)? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = BetsDetailListViewHolder(
+        LayoutInflater.from(parent.context).inflate(
+            R.layout.bet_detail_item,
+            parent,
+            false
+        ), printButtonListener
+    )
+}

+ 46 - 0
app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailListViewHolder.kt

@@ -0,0 +1,46 @@
+package com.khmer9.yuki.screen.detail
+
+import android.graphics.Color
+import android.view.View
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+
+class BetsDetailListViewHolder(itemView: View, private val printButtonListener: ((String, String, String, String, String, String, String) -> Unit)? = null) : BaseListViewHolder(itemView) {
+    override fun bindView(item: BaseListItem) {
+        when (item) {
+            is BettingDetailObject -> {
+                val txtFightBet: TextView = itemView.findViewById(R.id.txt_fight_no)
+                val txtTime: TextView = itemView.findViewById(R.id.txt_time)
+                val txtAmount: TextView = itemView.findViewById(R.id.txt_amount)
+                val txtType: TextView = itemView.findViewById(R.id.txt_type)
+
+                val fightNo = "#" + item.no.toString()
+                try {
+                    var datetime = item.time.split(" ")
+                    var time = datetime[1]
+                    var date = datetime[0]
+                    txtTime.text = date + System.getProperty("line.separator") + time
+                } catch (e: Exception) {
+                    txtTime.text = item.time
+                }
+
+                txtFightBet.text = fightNo
+                txtAmount.text = item.amount
+
+                try {
+                    val amount = item.amount.split("=")
+                    val winLose = amount[1].trim().toInt()
+                    if (winLose == 0) {
+                        txtAmount.setTextColor(Color.RED)
+                    } else {
+                        txtAmount.setTextColor(Color.rgb(48, 63, 159))
+                    }
+                } catch (e: Exception) {
+                }
+                txtType.text = item.betting
+            }
+        }
+    }
+}

+ 44 - 0
app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailViewModel.kt

@@ -0,0 +1,44 @@
+package com.khmer9.yuki.screen.detail
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.khmer9.yuki.app.getErrorCode
+import com.khmer9.yuki.base.BaseViewModel
+import com.khmer9.yuki.remote.service.ApiService
+import com.khmer9.yuki.util.PrefHelper
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+import java.util.concurrent.TimeUnit
+
+class BetsDetailViewModel(val apiService: ApiService, val prefHelper: PrefHelper) : BaseViewModel() {
+    private val _state = MutableLiveData(BetsDetailViewState())
+    val state: LiveData<BetsDetailViewState> = _state
+
+    fun getBetsDetailData(invoiceNum: String) {
+        if (_state.value!!.isProgress) return
+        _state.value = prev().copy(isProgress = true)
+        disposables.add(
+            apiService.getBettingDetail(invoiceNum)
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({
+                    if (it.resultCode == "1") {
+                        _state.value = prev().copy(
+                            isProgress = false,
+                            error = null,
+                            betDetailData = it.data
+                        )
+                    } else {
+                        _state.value =
+                            prev().copy(isProgress = false, error = "[${it.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    _state.value = prev().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    private fun prev() = _state.value!!
+}

+ 8 - 0
app/src/main/java/com/khmer9/yuki/screen/detail/BetsDetailViewState.kt

@@ -0,0 +1,8 @@
+package com.khmer9.yuki.screen.detail
+
+data class BetsDetailViewState(
+    val initial: Boolean = false,
+    val isProgress: Boolean = false,
+    val error: String? = null,
+    var betDetailData: BettingDetailData? = null,
+)

+ 81 - 0
app/src/main/java/com/khmer9/yuki/screen/detail/BettingDetailDataModel.kt

@@ -0,0 +1,81 @@
+package com.khmer9.yuki.screen.detail
+
+import com.gdtlib.lib.adapter.BaseListItem
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class BettingDetailResponse(
+    @Json(name = "code")
+    val resultCode: String,
+
+    @Json(name = "message")
+    val message: BetDetailMessage,
+
+    @Json(name = "data")
+    val data: BettingDetailData
+)
+
+@JsonClass(generateAdapter = true)
+data class BetDetailMessage(
+    @Json(name = "code")
+    val code: String,
+    @Json(name = "descriptions")
+    val description: String,
+)
+
+@JsonClass(generateAdapter = true)
+data class BettingDetailData(
+    @Json(name = "total_amount")
+    val totalAmount: String,
+
+    @Json(name = "total_amount_win")
+    val totalAmountWin: Double? = 0.0,
+
+    @Json(name = "total_cast")
+    val totalCast: Double? = 0.0,
+
+    @Json(name = "count")
+    val count: Int,
+
+    @Json(name = "list")
+    val bettingDetailList: List<BettingDetailObject>?
+)
+
+@JsonClass(generateAdapter = true)
+data class BettingDetailObject(
+    @Json(name = "_id")
+    val id: String,
+
+    @Json(name = "no")
+    val no: Int,
+
+    @Json(name = "betting")
+    val betting: String,
+
+    @Json(name = "bet_color")
+    val betColor: String,
+
+    @Json(name = "is_release_result")
+    val isReleaseResult: Boolean,
+
+    @Json(name = "bet_result")
+    val betResult: BetResult,
+
+    @Json(name = "return_amount")
+    val amount: String,
+
+    @Json(name = "time")
+    val time: String,
+) : BaseListItem() {
+    override fun getUnique(): String = id
+}
+
+@JsonClass(generateAdapter = true)
+data class BetResult(
+    @Json(name = "is_win")
+    val isWin: Boolean?,
+
+    @Json(name = "name")
+    val name: String?,
+)

+ 123 - 0
app/src/main/java/com/khmer9/yuki/screen/detail/BettingDetailFragment.kt

@@ -0,0 +1,123 @@
+package com.khmer9.yuki.screen.detail
+
+import android.graphics.Color
+import android.os.Bundle
+import android.view.View
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.base.BaseFragment
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.PrefHelper
+import kotlinx.android.synthetic.*
+import kotlinx.android.synthetic.main.betting_detail.*
+
+class BettingDetailFragment : BaseFragment(R.layout.betting_detail) {
+    private lateinit var adapter: BetsDetailAdapter
+    private lateinit var user: User
+    private lateinit var sharePref: PrefHelper
+    private lateinit var currency: String
+    private lateinit var betsDetailViewModel: BetsDetailViewModel
+    private val ARG_PARAM1 = "INVOICE_ID"
+    private val ARG_PARAM2 = "no"
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        sharePref = App.injectPrefHelper()
+        (requireActivity() as ShareActivity).apply {
+            user = loginData.user!!
+        }
+
+        betsDetailViewModel = BetsDetailViewModel(App.injectApiService(), App.injectPrefHelper())
+        when (user.currencyType) {
+            1 -> {//dollar
+                currency = "$"
+            }
+            2 -> {//riel
+                currency = "៛"
+            }
+            3 -> {//baht
+                currency = "฿"
+            }
+        }
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        btnClose.setOnClickListener {
+            it.btnClick().subscribe {
+                (requireActivity() as? ShareActivity)?.apply {
+                    onBackPressed()
+                }
+            }
+        }
+
+        requireArguments().getString(ARG_PARAM1)?.let {
+            betsDetailViewModel.getBetsDetailData(it)
+            //mtxtInvoiceNo.text = " #$it"
+        }
+
+        requireArguments().getString(ARG_PARAM2)?.let {
+            mtxtInvoiceNo.text = " #$it"
+        }
+
+
+        adapter = BetsDetailAdapter()
+        val layoutBlueManager = LinearLayoutManager(requireActivity() as? ShareActivity)
+        rv_current_bet.layoutManager = layoutBlueManager
+        rv_current_bet.itemAnimator = DefaultItemAnimator()
+        rv_current_bet.adapter = adapter
+        label_amount.text = resources.getString(R.string.amount) + " ($currency)"
+        betsDetailViewModel.state.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
+            getCurrentBets(it)
+        })
+    }
+
+    private fun getCurrentBets(it: BetsDetailViewState) {
+        if (it.betDetailData?.bettingDetailList != null) {
+            try {
+                when (user.currencyType) {
+                    1 -> {//dollar
+                        tv_turn_over.text = "${it.betDetailData?.totalCast} $"
+                        tv_total_win_lose.text = "${currencyFormat(it.betDetailData?.totalAmountWin!!)} $"
+                    }
+                    2 -> {//riel
+                        tv_turn_over.text = "${it.betDetailData?.totalCast} ៛"
+                        tv_total_win_lose.text = "${currencyFormat(it.betDetailData?.totalAmountWin!!)} ៛"
+                    }
+                    3 -> {//baht
+                        tv_turn_over.text = "${it.betDetailData?.totalCast} ฿"
+                        tv_total_win_lose.text = "${currencyFormat(it.betDetailData?.totalAmountWin!!)} ฿"
+                    }
+                }
+            } catch (e: Exception) {
+            }
+
+            try {
+                if (it.betDetailData?.totalAmountWin!! > 0) {
+                    tv_turn_over.setTextColor(Color.parseColor("#303F9F"))
+                    tv_total_win_lose.setTextColor(Color.parseColor("#303F9F"))
+                } else {
+                    tv_turn_over.setTextColor(Color.parseColor("#C62828"))
+                    tv_total_win_lose.setTextColor(Color.parseColor("#C62828"))
+                }
+            } catch (e: Exception) {
+                tv_turn_over.setTextColor(Color.parseColor("#303F9F"))
+                tv_total_win_lose.setTextColor(Color.parseColor("#303F9F"))
+            }
+
+            adapter.submitList(it.betDetailData?.bettingDetailList)
+        }
+        adapter.notifyDataSetChanged()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        this.clearFindViewByIdCache()
+    }
+}

+ 69 - 0
app/src/main/java/com/khmer9/yuki/screen/login/LoginActivity.kt

@@ -0,0 +1,69 @@
+package com.khmer9.yuki.screen.login
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.Observer
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import kotlinx.android.synthetic.main.activity_login.*
+
+class LoginActivity : AppCompatActivity() {
+    private lateinit var loginViewModel: LoginViewModel
+    private var user: User? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        loginViewModel = LoginViewModel(App.injectApiService(), App.injectPrefHelper())
+
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_login)
+
+        user = ModelPreferencesManager.get<User>(Const.USER_KEY)
+        if (user != null) et_username.setText(user?.userName)
+
+        loginViewModel.state.observe(this, Observer(this::render))
+
+        btn_login.setOnClickListener {
+            til_username.isErrorEnabled = false
+            til_username.error = null
+            til_password.isErrorEnabled = false
+            til_password.error = null
+            val username = et_username.text
+            val password = et_password.text
+            if (username.isNullOrEmpty()) {
+                til_username.error = getString(R.string.err_not_empty)
+                til_username.requestFocus()
+                return@setOnClickListener
+            }
+            if (password.isNullOrEmpty()) {
+                til_password.error = getString(R.string.err_not_empty)
+                til_password.requestFocus()
+                return@setOnClickListener
+            }
+            loginViewModel.login(username.toString(), password.toString())
+        }
+
+    }
+
+    private fun render(state: LoginViewState) {
+        if (state.isProgress) {
+            tv_error.text = ""
+        }
+        if (state.isLoginSuccess) {
+            val intent = Intent(this, ShareActivity::class.java)
+            startActivity(intent)
+            finishAffinity()
+        }
+        if (state.error != null) {
+            tv_error.text = state.error
+        }
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+    }
+}

+ 68 - 0
app/src/main/java/com/khmer9/yuki/screen/login/LoginViewModel.kt

@@ -0,0 +1,68 @@
+package com.khmer9.yuki.screen.login
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.khmer9.yuki.app.getErrorCode
+import com.khmer9.yuki.base.BaseViewModel
+import com.khmer9.yuki.remote.LoginRequest
+import com.khmer9.yuki.remote.service.ApiService
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import com.khmer9.yuki.util.PrefHelper
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+import java.util.concurrent.TimeUnit
+
+class LoginViewModel(val apiService: ApiService, val prefHelper: PrefHelper) : BaseViewModel() {
+    private val _state = MutableLiveData<LoginViewState>(LoginViewState())
+    val state: LiveData<LoginViewState> = _state
+
+    fun login(username: String, password: String) {
+        if (_state.value!!.isProgress) return
+        val request = LoginRequest(username, password)
+        _state.value = prev().copy(isProgress = true)
+
+        disposables.add(
+            apiService.login(request)
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({
+                    if (it.resultCode == "1") {
+                        _state.value = prev().copy(isProgress = false, isLoginSuccess = true)
+                        prefHelper.setUserToken(it.data?.token ?: "")
+                        prefHelper.setMinBetting(it.data?.user?.amountMin ?: 0)
+                        prefHelper.setMaxBetting(it.data?.user?.amountMax ?: 0)
+                        ModelPreferencesManager.put(it.data, Const.USER_KEY)
+                        getLottoSet()
+                    } else {
+                        _state.value = prev().copy(isProgress = false, error = "[${it.message.description}]")
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    _state.value = prev().copy(isProgress = false, error = message)
+                })
+        )
+    }
+
+    private fun getLottoSet() {
+        disposables.add(
+            apiService.remoteLottoSet()
+                .timeout(60, TimeUnit.SECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({
+                    Log.d("bettingType", it.toString())
+                    if (it.resultCode == "1") {
+                        it.data?.let { lottoList -> ModelPreferencesManager.putList(lottoList, Const.LOTTO_KEY) }
+                    }
+                }, {
+                    val message: String = "ប្រតិបត្តិការមិនជោគជ័យ\n" + it.getErrorCode()
+                    Log.d("bettingType", message)
+                })
+        )
+    }
+
+    private fun prev() = _state.value!!
+}

+ 9 - 0
app/src/main/java/com/khmer9/yuki/screen/login/LoginViewState.kt

@@ -0,0 +1,9 @@
+package com.khmer9.yuki.screen.login
+
+data class LoginViewState(
+    val initial: Boolean = false,
+    val isProgress: Boolean = false,
+    val isLoginSuccess: Boolean = false,
+
+    val error: String? = null
+)

+ 21 - 0
app/src/main/java/com/khmer9/yuki/screen/main/MainAdapter.kt

@@ -0,0 +1,21 @@
+package com.khmer9.yuki.screen.main
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+
+class MainAdapter() : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null
+    var detailButtonListener: ((String, String) -> Unit)? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
+        MainListViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.item_table_holder,
+                parent,
+                false
+            ), printButtonListener, detailButtonListener
+        )
+}

+ 27 - 0
app/src/main/java/com/khmer9/yuki/screen/main/MainAdapterHeader.kt

@@ -0,0 +1,27 @@
+package com.khmer9.yuki.screen.main
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.khmer9.yuki.R
+
+class MainAdapterHeader : RecyclerView.Adapter<MainAdapterHeader.ViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent.context)
+            .inflate(R.layout.layout_main_adapter_header, parent, false)
+
+        return ViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+    }
+
+    override fun getItemCount(): Int {
+        return 1;
+    }
+
+    class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
+    }
+}

+ 778 - 0
app/src/main/java/com/khmer9/yuki/screen/main/MainFragment.kt

@@ -0,0 +1,778 @@
+package com.khmer9.yuki.screen.main
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.Typeface
+import android.graphics.drawable.AnimationDrawable
+import android.graphics.drawable.Drawable
+import android.media.MediaPlayer
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.view.animation.AlphaAnimation
+import android.view.animation.Animation
+import android.widget.ImageView
+import android.widget.TextView
+import android.widget.Toast
+import androidx.constraintlayout.widget.Group
+import androidx.core.content.ContextCompat
+import androidx.core.content.ContextCompat.getColor
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.ConcatAdapter
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.ahmedelsayed.sunmiprinterutill.PrintMe
+import com.gdtlib.lib.adapter.BaseListItem
+import com.google.android.material.textview.MaterialTextView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.adapter.BettingStatementHeaderAdapter
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.app.ShareViewState
+import com.khmer9.yuki.base.BaseFragment
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.extension.random
+import com.khmer9.yuki.extension.waitingLotto
+import com.khmer9.yuki.extension.zoomIn
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.remote.main.SocketClientNewGameDataModel
+import com.khmer9.yuki.remote.main.SocketClientRandomDataModel
+import com.khmer9.yuki.remote.main.SocketClientResultDataModel
+import com.khmer9.yuki.remote.main.SocketClientTimeDataModel
+import com.khmer9.yuki.remote.service.ConnectivityStates
+import com.khmer9.yuki.screen.detail.BettingDetailFragment
+import com.khmer9.yuki.screen.play.PlayFragment
+import com.khmer9.yuki.screen.report.StatementFragment
+import com.khmer9.yuki.screen.result.ResultFragment
+import com.khmer9.yuki.screen.setting.SettingFragment
+import com.khmer9.yuki.screen.splash.SplashScreenActivity
+import com.khmer9.yuki.view.LottoView
+import com.khmer9.yuki.view.SettingButtonView
+import com.mazenrashed.printooth.Printooth
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import io.reactivex.Completable
+import kotlinx.android.synthetic.main.fragment_main.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+/**
+ * A simple [Fragment] subclass as the default destination in the navigation.
+ */
+class MainFragment : BaseFragment(R.layout.layout_fragment_main_yuki) {
+    private val shareViewModel = App.injectShareViewModel()
+    private val sharePref = App.injectPrefHelper()
+    private lateinit var printMe: PrintMe
+    private lateinit var adapter: MainAdapter
+    private lateinit var adapterHeader: MainAdapterHeader
+    private lateinit var lottoView1: LottoView
+    private lateinit var lottoView2: LottoView
+    private lateinit var lottoView3: LottoView
+    private lateinit var lottoView4: LottoView
+    private lateinit var lottoView5: LottoView
+    private lateinit var lottoView6: LottoView
+    private lateinit var lottoView7: LottoView
+    private lateinit var lottoView8: LottoView
+    private lateinit var lottoView9: LottoView
+    private lateinit var lottoView10: LottoView
+    private lateinit var lottoView11: LottoView
+    private lateinit var lottoView12: LottoView
+    private lateinit var lottoView13: LottoView
+    private lateinit var lottoView14: LottoView
+    private lateinit var lottoView15: LottoView
+    private lateinit var lottoView16: LottoView
+    private lateinit var lottoView17: LottoView
+    private lateinit var lottoView18: LottoView
+    private lateinit var lottoView19: LottoView
+    private lateinit var lottoView20: LottoView
+    private lateinit var lottoTv1: TextView
+    private lateinit var lottoTv2: TextView
+    private lateinit var lottoTv3: TextView
+    private lateinit var lottoTv4: TextView
+    private lateinit var lottoTv5: TextView
+    private lateinit var lottoTv6: TextView
+    private lateinit var lottoTv7: TextView
+    private lateinit var lottoTv8: TextView
+    private lateinit var lottoTv9: TextView
+    private lateinit var lottoTv10: TextView
+    private lateinit var lottoTv11: TextView
+    private lateinit var lottoTv12: TextView
+    private lateinit var lottoTv13: TextView
+    private lateinit var lottoTv14: TextView
+    private lateinit var lottoTv15: TextView
+    private lateinit var lottoTv16: TextView
+    private lateinit var lottoTv17: TextView
+    private lateinit var lottoTv18: TextView
+    private lateinit var lottoTv19: TextView
+    private lateinit var lottoTv20: TextView
+    private lateinit var tvResult: TextView
+    private lateinit var tvResultSmall: TextView
+    private lateinit var vBigSmal: TextView
+    private lateinit var vRang: TextView
+    private lateinit var ivBigLogo: ImageView
+    private lateinit var tvBigLogo: TextView
+    private lateinit var lottoGroup: Group
+    private lateinit var arrayLottoView: ArrayList<LottoView>
+    private lateinit var arrayLottoTextView: ArrayList<TextView>
+    private lateinit var user: User
+    private var resultLength: Int = 0
+    private var lottoAnimationDrawable: AnimationDrawable? = null
+    private lateinit var btnPlay: SettingButtonView
+    private lateinit var btnPlayFrame: View
+    private lateinit var betPlease: MediaPlayer
+    private lateinit var stopBet: MediaPlayer
+    private lateinit var stopBet2: MediaPlayer
+    private lateinit var betTouch: MediaPlayer
+    private lateinit var countDown: MediaPlayer
+    private lateinit var countDown1: MediaPlayer
+    private lateinit var countDown2: MediaPlayer
+    private lateinit var countDown3: MediaPlayer
+    private lateinit var countDown4: MediaPlayer
+    private lateinit var countDown5: MediaPlayer
+    private lateinit var countDown6: MediaPlayer
+    private lateinit var countDown7: MediaPlayer
+    private lateinit var countDown8: MediaPlayer
+    private lateinit var countDown9: MediaPlayer
+    private lateinit var countDown10: MediaPlayer
+    private lateinit var congrat: MediaPlayer
+    private lateinit var moshi: Moshi
+    private lateinit var goodLuck: MediaPlayer
+    lateinit var result: MediaPlayer
+    private var bgDefaultLotto: Drawable? = null
+    private var bgBlueLotto: Drawable? = null
+    private var bgGreenLotto: Drawable? = null
+    private var bgPinkLotto: Drawable? = null
+    private var bgYellowLotto: Drawable? = null
+    private var bgWhiteLotto: Drawable? = null
+    private var bgCircleBlue: Drawable? = null
+    private var bgCircleRed: Drawable? = null
+    private var bgCircleGreen: Drawable? = null
+    private var bgCircleYellow: Drawable? = null
+    private var bgCirclePink: Drawable? = null
+    private var textBig: Drawable? = null
+    private var textSmall: Drawable? = null
+    private var colorWhite: Int? = null
+    private var colorRed: Int? = null
+    private var anim: AlphaAnimation? = null
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        printMe = PrintMe(context)
+        //companyInfoId =arguments?.getInt(COM_INTO_ID_KEY) ?: 0
+        (requireActivity() as ShareActivity).apply {
+            betPlease = MediaPlayer.create(this, R.raw.start_betting)
+            stopBet = MediaPlayer.create(this, R.raw.time_stop_betting)
+            stopBet2 = MediaPlayer.create(this, R.raw.stop_bet)
+            goodLuck = MediaPlayer.create(this, R.raw.goodluck)
+            betTouch = MediaPlayer.create(this, R.raw.bettouch)
+            countDown = MediaPlayer.create(this, R.raw.countdown)
+            result = MediaPlayer.create(this, R.raw.result)
+
+            countDown1 = MediaPlayer.create(this, R.raw.countdown)
+            countDown2 = MediaPlayer.create(this, R.raw.countdown)
+            countDown3 = MediaPlayer.create(this, R.raw.countdown)
+            countDown4 = MediaPlayer.create(this, R.raw.countdown)
+            countDown5 = MediaPlayer.create(this, R.raw.countdown)
+            countDown6 = MediaPlayer.create(this, R.raw.countdown)
+            countDown7 = MediaPlayer.create(this, R.raw.countdown)
+            countDown8 = MediaPlayer.create(this, R.raw.countdown)
+            countDown9 = MediaPlayer.create(this, R.raw.countdown)
+            countDown10 = MediaPlayer.create(this, R.raw.countdown)
+            congrat = MediaPlayer.create(this, R.raw.cong)
+
+            user = loginData.user!!
+        }
+        bgDefaultLotto = ContextCompat.getDrawable(requireContext(), R.drawable.ic_logo)
+        bgBlueLotto = ContextCompat.getDrawable(requireContext(), R.drawable.ic_blue_circle)
+        bgPinkLotto = ContextCompat.getDrawable(requireContext(), R.drawable.ic_pink_circle)
+        bgYellowLotto = ContextCompat.getDrawable(requireContext(), R.drawable.ic_yellow_circle)
+        bgGreenLotto = ContextCompat.getDrawable(requireContext(), R.drawable.ic_green_circle)
+        bgWhiteLotto = ContextCompat.getDrawable(requireContext(), R.drawable.bg_circle_white)
+        bgCircleBlue = ContextCompat.getDrawable(requireContext(), R.drawable.bg_circle_blue_stroke)
+        bgCircleRed = ContextCompat.getDrawable(requireContext(), R.drawable.bg_circle_red_stroke)
+        bgCirclePink = ContextCompat.getDrawable(requireContext(), R.drawable.bg_circle_pink_stroke)
+        bgCircleYellow = ContextCompat.getDrawable(requireContext(), R.drawable.bg_circle_yellow_stroke)
+        bgCircleGreen = ContextCompat.getDrawable(requireContext(), R.drawable.bg_circle_green_stroke)
+        textBig = ContextCompat.getDrawable(requireContext(), R.drawable.text_main_big)
+        textSmall = ContextCompat.getDrawable(requireContext(), R.drawable.text_main_small)
+        colorWhite = getColor(requireContext(), R.color.color_white)
+        colorRed = getColor(requireContext(), R.color.color_red_600)
+        anim = AlphaAnimation(0.0f, 1.0f)
+        anim?.duration = 50 //You can manage the blinking time with this parameter
+        anim?.startOffset = 50
+        anim?.repeatMode = Animation.REVERSE
+        anim?.repeatCount = Animation.INFINITE
+        moshi = Moshi.Builder().build()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        (requireActivity() as ShareActivity).apply {
+            userNameTextView.text = user.userName
+        }
+        shareViewModel.getConnectivityStates.observe(viewLifecycleOwner, Observer {
+            getSocketConnectionStatus(it)
+        })
+        //------------------------------------------------ New Socket Listener
+        shareViewModel.getClientTime.observe(viewLifecycleOwner, Observer {
+            if (!it.isNullOrEmpty()) {
+                val adapter: JsonAdapter<SocketClientTimeDataModel> = moshi.adapter(
+                    SocketClientTimeDataModel::class.java
+                )
+                val socketClientTimeResponse = adapter.fromJson(it)
+                if (socketClientTimeResponse != null) {
+                    getLottoMessage(socketClientTimeResponse)
+                    btnPlay.isEnabled = true
+                }
+            }
+        })
+        shareViewModel.getClientNewGame.observe(viewLifecycleOwner, Observer {
+            if (!it.isNullOrEmpty()) {
+                val adapter: JsonAdapter<SocketClientNewGameDataModel> = moshi.adapter(
+                    SocketClientNewGameDataModel::class.java
+                )
+                val socketClientNewGameResponse = adapter.fromJson(it)
+                if (socketClientNewGameResponse != null) {
+                    //Split time 2022-04-11T13:46:56.000Z => [2022-04-11] [13:46:56.000Z]
+                    val date = socketClientNewGameResponse.date.split("T")[0].toString()
+                    tv_game_number.text = date + "#" + socketClientNewGameResponse.no.toString()
+
+                    ivBigLogo.setImageResource(R.drawable.ic_logo_big)
+                    ivBigLogo.invalidate()
+                    btnPlay.isEnabled = true
+
+                    // Reset Recyclerview at Current Betting
+                    this.adapter = MainAdapter()
+                    shareViewModel.remoteBettingCurrentMobileAPI()
+                }
+
+                //
+            }
+        })
+        shareViewModel.getClientRandom.observe(viewLifecycleOwner, Observer {
+            if (!it.isNullOrEmpty()) {
+                val adapter: JsonAdapter<SocketClientRandomDataModel> = moshi.adapter(
+                    SocketClientRandomDataModel::class.java
+                )
+                val socketClientRandomResponse = adapter.fromJson(it)
+                if (socketClientRandomResponse != null) {
+                    getLottoRandom(socketClientRandomResponse)
+                }
+
+                if (btnPlay.isEnabled) {
+                    btnPlay.isEnabled = false
+                }
+            }
+        })
+        shareViewModel.getClientResult.observe(viewLifecycleOwner, Observer {
+            if (!it.isNullOrEmpty()) {
+                val adapter: JsonAdapter<SocketClientResultDataModel> = moshi.adapter(
+                    SocketClientResultDataModel::class.java
+                )
+                val socketClientResultResponse = adapter.fromJson(it)
+                if (socketClientResultResponse != null) {
+                    getLottoResultMessage(socketClientResultResponse)
+                }
+            }
+        })
+        //------------------------
+        shareViewModel.getShareState.observe(viewLifecycleOwner, Observer {
+            getMain(it)
+        })
+        lottoView1 = view.findViewById(R.id.v1)
+        lottoView2 = view.findViewById(R.id.v2)
+        lottoView3 = view.findViewById(R.id.v3)
+        lottoView4 = view.findViewById(R.id.v4)
+        lottoView5 = view.findViewById(R.id.v5)
+        lottoView6 = view.findViewById(R.id.v6)
+        lottoView7 = view.findViewById(R.id.v7)
+        lottoView8 = view.findViewById(R.id.v8)
+        lottoView9 = view.findViewById(R.id.v9)
+        lottoView10 = view.findViewById(R.id.v10)
+        lottoView11 = view.findViewById(R.id.v11)
+        lottoView12 = view.findViewById(R.id.v12)
+        lottoView13 = view.findViewById(R.id.v13)
+        lottoView14 = view.findViewById(R.id.v14)
+        lottoView15 = view.findViewById(R.id.v15)
+        lottoView16 = view.findViewById(R.id.v16)
+        lottoView17 = view.findViewById(R.id.v17)
+        lottoView18 = view.findViewById(R.id.v18)
+        lottoView19 = view.findViewById(R.id.v19)
+        lottoView20 = view.findViewById(R.id.v20)
+
+        lottoTv1 = view.findViewById(R.id.tv1)
+        lottoTv2 = view.findViewById(R.id.tv2)
+        lottoTv3 = view.findViewById(R.id.tv3)
+        lottoTv4 = view.findViewById(R.id.tv4)
+        lottoTv5 = view.findViewById(R.id.tv5)
+        lottoTv6 = view.findViewById(R.id.tv6)
+        lottoTv7 = view.findViewById(R.id.tv7)
+        lottoTv8 = view.findViewById(R.id.tv8)
+        lottoTv9 = view.findViewById(R.id.tv9)
+        lottoTv10 = view.findViewById(R.id.tv10)
+        lottoTv11 = view.findViewById(R.id.tv11)
+        lottoTv12 = view.findViewById(R.id.tv12)
+        lottoTv13 = view.findViewById(R.id.tv13)
+        lottoTv14 = view.findViewById(R.id.tv14)
+        lottoTv15 = view.findViewById(R.id.tv15)
+        lottoTv16 = view.findViewById(R.id.tv16)
+        lottoTv17 = view.findViewById(R.id.tv17)
+        lottoTv18 = view.findViewById(R.id.tv18)
+        lottoTv19 = view.findViewById(R.id.tv19)
+        lottoTv20 = view.findViewById(R.id.tv20)
+
+        arrayLottoView = arrayListOf(
+            lottoView1,
+            lottoView2,
+            lottoView3,
+            lottoView4,
+            lottoView5,
+            lottoView6,
+            lottoView7,
+            lottoView8,
+            lottoView9,
+            lottoView10,
+            lottoView11,
+            lottoView12,
+            lottoView13,
+            lottoView14,
+            lottoView15,
+            lottoView16,
+            lottoView17,
+            lottoView18,
+            lottoView19,
+            lottoView20
+        )
+
+        arrayLottoTextView = arrayListOf(
+            lottoTv1,
+            lottoTv2,
+            lottoTv3,
+            lottoTv4,
+            lottoTv5,
+            lottoTv6,
+            lottoTv7,
+            lottoTv8,
+            lottoTv9,
+            lottoTv10,
+            lottoTv11,
+            lottoTv12,
+            lottoTv13,
+            lottoTv14,
+            lottoTv15,
+            lottoTv16,
+            lottoTv17,
+            lottoTv18,
+            lottoTv19,
+            lottoTv20
+        )
+
+        tvResult = view.findViewById(R.id.tv_result_show)
+        tvResultSmall = view.findViewById(R.id.tv_result_number)
+        ivBigLogo = view.findViewById(R.id.iv_big_logo)
+        tvBigLogo = view.findViewById(R.id.tv_big_logo)
+        lottoGroup = view.findViewById(R.id.group_lotto)
+        vBigSmal = view.findViewById(R.id.v_big_small)
+        vRang = view.findViewById(R.id.v_range)
+
+        btnPlay = view.findViewById(R.id.btn_play)
+        btnPlayFrame = view.findViewById(R.id.v_frame_play)
+
+        btnPlay.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+
+                (requireActivity() as? ShareActivity)?.apply {
+                    supportFragmentManager.beginTransaction()
+                        .setCustomAnimations(
+                            R.anim.slide_in,
+                            R.anim.slide_out,
+                            R.anim.slide_in_exit,
+                            R.anim.slide_out_exit
+                        )
+                        .add(R.id.container, PlayFragment().apply {
+                            arguments =
+                                Bundle().apply {
+                                    // putInt(Fragment7.COM_INFO_ID_KEY, companyInfoId)
+                                }
+
+                        })
+                        .addToBackStack(null)
+                        .commit()
+                }
+            }
+        }
+
+        view.findViewById<SettingButtonView>(R.id.btn_result).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                (requireActivity() as? ShareActivity)?.apply {
+                    supportFragmentManager.beginTransaction()
+                        .setCustomAnimations(
+                            R.anim.slide_in,
+                            R.anim.slide_out,
+                            R.anim.slide_in_exit,
+                            R.anim.slide_out_exit
+                        )
+                        .add(R.id.container, ResultFragment().apply {
+                            arguments =
+                                Bundle().apply {
+                                    // putInt(Fragment7.COM_INFO_ID_KEY, companyInfoId)
+                                }
+
+                        })
+                        .addToBackStack(null)
+                        .commit()
+                }
+            }
+        }
+
+        (requireActivity() as? ShareActivity)?.apply {
+            groupToolbarIcons.isVisible = true
+
+            settingImageView.setOnClickListener {
+                if (!sharePref.getIsMute()) betTouch.start()
+                it.btnClick().subscribe {
+                    supportFragmentManager.beginTransaction()
+                        .setCustomAnimations(
+                            R.anim.slide_in,
+                            R.anim.slide_out,
+                            R.anim.slide_in_exit,
+                            R.anim.slide_out_exit
+                        )
+                        .add(R.id.container, SettingFragment())
+                        .addToBackStack(null)
+                        .commit()
+                }
+            }
+
+            refreshImageView.setOnClickListener {
+                shareViewModel.remoteMainScreenAPI()
+            }
+
+            reportImageView.setOnClickListener {
+                if (!sharePref.getIsMute()) betTouch.start()
+                it.btnClick().subscribe {
+                    supportFragmentManager.beginTransaction()
+                        .setCustomAnimations(
+                            R.anim.slide_in,
+                            R.anim.slide_out,
+                            R.anim.slide_in_exit,
+                            R.anim.slide_out_exit
+                        )
+                        .add(R.id.container, StatementFragment().apply {
+                            arguments =
+                                Bundle().apply {
+                                    // putInt(Fragment7.COM_INFO_ID_KEY, companyInfoId)
+                                }
+
+                        })
+                        .addToBackStack(null)
+                        .commit()
+                }
+            }
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    override fun onActivityCreated(savedInstanceState: Bundle?) {
+        super.onActivityCreated(savedInstanceState)
+        //viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
+    }
+
+    private fun animateSmallLogo() {
+        GlobalScope.launch(Dispatchers.Main) {
+            var i = 0
+            while (i < 20) {
+                arrayLottoTextView[i].background = bgDefaultLotto
+                arrayLottoTextView[i].waitingLotto().subscribe()
+                i++
+                delay(5)
+            }
+        }
+    }
+
+    private fun animateLottoView(lottoTextView: TextView, number: Int): Completable {
+        return Completable.create {
+            lottoTextView.setBackgroundResource(R.drawable.spin_animation)
+            lottoAnimationDrawable = lottoTextView.background as AnimationDrawable
+
+            lottoAnimationDrawable!!.start()
+
+            GlobalScope.launch(Dispatchers.Main) {
+                var i = 0
+                while (i < 12) {
+                    val rdns = (1..80).random()
+                    lottoTextView.text = "$rdns"
+
+                    i++
+                    delay(100)
+                    if (i == 12) {
+                        when (number) {
+                            in 1..20 -> {
+                                lottoTextView.background = bgBlueLotto
+                            }
+                            in 21..40 -> {
+                                lottoTextView.background = bgYellowLotto
+                            }
+                            in 41..60 -> {
+                                lottoTextView.background = bgPinkLotto
+                            }
+                            else -> {
+                                lottoTextView.background = bgGreenLotto
+                            }
+                        }
+                        lottoTextView.setText("$number")
+                        it.onComplete()
+                    }
+                }
+            }
+        }
+    }
+
+    private fun animateALotto(lottoTextView: TextView, number: Int): Completable {
+        return animateLottoView(lottoTextView, number)
+            .andThen(animateLottoZoom(lottoTextView))
+    }
+
+    private fun animateLottoZoom(view: View): Completable {
+        return view.zoomIn()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        Log.d("MainFagment", "onResume")
+    }
+
+    @SuppressLint("ResourceAsColor", "CheckResult", "SetTextI18n")
+    fun getLottoMessage(lotto: SocketClientTimeDataModel) {
+        if (lotto.isCountDowning()) {
+            //if (ivBigLogo.isInvisible) ivBigLogo.isVisible = true
+            tvResult.text = "${lotto.time}"
+            tvResult.setTextColor(Color.WHITE)
+
+            when (lotto.time) {
+                48 -> shareViewModel.remoteMainScreenAPI()
+                118 -> if (!sharePref.getIsMute()) betPlease.start()
+            }
+        }
+    }
+
+    private fun getLottoRandom(lotto: SocketClientRandomDataModel) {
+        tvResult.text = "${lotto.randomTime}"
+        tvResult.setTextColor(Color.RED)
+        btnPlay.isEnabled = false
+
+        when (lotto.randomTime) {
+            0 -> if (!sharePref.getIsMute()) stopBet2.start()
+            1 -> if (!sharePref.getIsMute()) countDown1.start()
+            2 -> if (!sharePref.getIsMute()) countDown2.start()
+            3 -> if (!sharePref.getIsMute()) countDown3.start()
+            4 -> if (!sharePref.getIsMute()) countDown4.start()
+            5 -> if (!sharePref.getIsMute()) countDown5.start()
+            6 -> if (!sharePref.getIsMute()) countDown6.start()
+            7 -> if (!sharePref.getIsMute()) countDown7.start()
+            8 -> if (!sharePref.getIsMute()) countDown8.start()
+            9 -> if (!sharePref.getIsMute()) countDown9.start()
+            10 -> {
+                stopBet.start()
+                shareViewModel.remoteMainScreenAPI()
+            }
+        }
+    }
+
+    private fun getLottoResultMessage(lotto: SocketClientResultDataModel) {
+        if (!sharePref.getIsMute()) result.start()
+
+        if (lotto.bonusBetweenYear.isBonus) {
+            tv_bonus.text = "(x${lotto.bonusBetweenYear.payout})"
+            tv_bonus.startAnimation(anim)
+        }
+
+        //setImageBetweenYearResult(lotto.betweenYear.yearNumber)
+        animateALotto(lotto.betweenYear.yearNumber)
+
+        tvResult.text = lotto.betweenYear.yearNameKh
+        vBigSmal.text = lotto.smallLarge.yearNameKh
+        vBigSmal.setTypeface(null, Typeface.BOLD)
+        vBigSmal.setTextColor(Color.WHITE)
+        vRang.text = lotto.range.yearNameEn
+        vRang.setTypeface(null, Typeface.BOLD)
+        vRang.setTextColor(Color.WHITE)
+        tv_result_number.text = lotto.smallLarge.yearNameKh + " " + lotto.range.yearNameKh + "-" + lotto.betweenYear.yearNameKh
+    }
+
+    fun animateALotto(number: Int) {
+        GlobalScope.launch(Dispatchers.Main) {
+            var i = 0
+            while (i < 12) {
+                setImageBetweenYearResult(i + 1)
+                i++
+                delay(100)
+                if (i == 12) {
+                    setImageBetweenYearResult(number)
+                }
+            }
+        }
+    }
+
+    private fun setImageBetweenYearResult(yearNumber: Int) {
+        when (yearNumber) {
+            1 -> {
+                Log.d("between", "1")
+                ivBigLogo.setImageResource(R.drawable.animal_one)
+            }
+            2 -> {
+                Log.d("between", "2")
+                ivBigLogo.setImageResource(R.drawable.animal_two)
+            }
+            3 -> {
+                Log.d("between", "3")
+                ivBigLogo.setImageResource(R.drawable.animal_three)
+            }
+            4 -> {
+                Log.d("between", "4")
+                ivBigLogo.setImageResource(R.drawable.animal_four)
+            }
+            5 -> {
+                Log.d("between", "5")
+                ivBigLogo.setImageResource(R.drawable.animal_five)
+            }
+            6 -> {
+                Log.d("between", "6")
+                ivBigLogo.setImageResource(R.drawable.animal_six)
+            }
+            7 -> {
+                Log.d("between", "7")
+                ivBigLogo.setImageResource(R.drawable.animal_seven)
+            }
+            8 -> {
+                Log.d("between", "8")
+                ivBigLogo.setImageResource(R.drawable.animal_eight)
+            }
+            9 -> {
+                Log.d("between", "9")
+                ivBigLogo.setImageResource(R.drawable.animal_nine)
+            }
+            10 -> {
+                Log.d("between", "10")
+                ivBigLogo.setImageResource(R.drawable.animal_ten)
+            }
+            11 -> {
+                Log.d("between", "11")
+                ivBigLogo.setImageResource(R.drawable.animal_eleven)
+            }
+            12 -> {
+                Log.d("between", "12")
+                ivBigLogo.setImageResource(R.drawable.animal_twelve)
+            }
+            else -> {
+                ivBigLogo.setImageResource(R.drawable.ic_logo_big)
+            }
+        }
+        ivBigLogo.invalidate()
+    }
+
+    private fun getSocketConnectionStatus(status: Int) {
+        when (status) {
+            ConnectivityStates.STATE_DISCONNECTED -> {
+                Log.d("Socket", "onDisconnect...")
+
+                val mStartActivity = Intent(requireContext(), SplashScreenActivity::class.java)
+                startActivity(mStartActivity)
+                System.exit(0)
+            }
+            ConnectivityStates.STATE_NOT_CONNECTED -> {
+                Log.d("Socket", "onNotconnected...")
+                Toast.makeText(requireContext(), "មិនមាន Internet", Toast.LENGTH_LONG).show()
+            }
+            ConnectivityStates.STATE_RECONNECTING -> {
+                Log.d("Socket", "onReconnecting...")
+            }
+            ConnectivityStates.STATE_TIMEOUT -> {
+                Log.d("Socket", "onTimout...")
+                val intent = Intent(requireContext(), SplashScreenActivity::class.java)
+                startActivity(intent)
+                requireActivity().finishAffinity()
+            }
+            ConnectivityStates.STATE_CONNECTED -> {
+                Log.d("Socket", "onConnected...")
+                shareViewModel.remoteMainScreenAPI()
+            }
+        }
+    }
+
+    private fun getMain(state: ShareViewState) {
+        if (!state.previousBettingList.isNullOrEmpty()) {
+            adapter = MainAdapter()
+            adapterHeader = MainAdapterHeader()
+            adapter.submitList(state.previousBettingList as List<BaseListItem>?)
+            Log.d("myBettingMobileListMAIN", "" + state.previousBettingList.toString())
+            rvCurrentBetting.setHasFixedSize(true)
+            rvCurrentBetting.layoutManager = LinearLayoutManager(context)
+            val concatAdapter = ConcatAdapter(adapterHeader, adapter)
+            rvCurrentBetting.adapter = concatAdapter
+            adapter.printButtonListener = { ticketID, gameID, betDate, betType, betCast, winLose ->
+                if (Printooth.hasPairedPrinter()) {
+                    printSomePrintable(
+                        user.userName,
+                        ticketID,
+                        gameID,
+                        betDate,
+                        betType,
+                        betCast,
+                        winLose
+                    )
+                } else {
+                    val views = layoutInflater.inflate(R.layout.invoice_print, null)
+                    views.findViewById<MaterialTextView>(R.id.title).text = user.userName
+                    views.findViewById<MaterialTextView>(R.id.ticketId).text = ticketID
+                    views.findViewById<MaterialTextView>(R.id.gameId).text = gameID
+                    views.findViewById<MaterialTextView>(R.id.date).text = betDate
+                    views.findViewById<MaterialTextView>(R.id.betType).text = betType
+                    views.findViewById<MaterialTextView>(R.id.betPoint).text = betCast
+                    views.findViewById<MaterialTextView>(R.id.winLose).text = winLose
+                    printMe.sendViewToPrinter(views)
+                }
+            }
+
+            adapter.detailButtonListener = { invoiceId, invoiceNo ->
+                if (!invoiceId.isNullOrEmpty()) {
+                    (requireActivity() as? ShareActivity)?.apply {
+                        supportFragmentManager.beginTransaction()
+                            .setCustomAnimations(
+                                R.anim.slide_in,
+                                R.anim.slide_out,
+                                R.anim.slide_in_exit,
+                                R.anim.slide_out_exit
+                            )
+                            .add(R.id.container, BettingDetailFragment().apply {
+                                arguments = Bundle().apply {
+                                    putString("INVOICE_ID", invoiceId)
+                                    putString("no", invoiceNo)
+                                }
+                            })
+                            .addToBackStack(null)
+                            .commit()
+                    }
+                }
+            }
+        }
+
+        if (state.accountBalance != null) {
+            (requireActivity() as? ShareActivity)?.apply {
+                balanceTextView.text = currencyFormat(state.accountBalance.toDouble())
+            }
+        }
+        tv_game_number.text = shareViewModel.getShareState.value?.nextNo
+        tv_result_number.text = shareViewModel.getShareState.value?.lastResult.toString()
+    }
+}
+

+ 10 - 0
app/src/main/java/com/khmer9/yuki/screen/main/MainListItem.kt

@@ -0,0 +1,10 @@
+package com.khmer9.yuki.screen.main
+
+import com.gdtlib.lib.adapter.BaseListItem
+
+data class MainListItem(
+    val Id: Int,
+
+    ) : BaseListItem() {
+    override fun getUnique(): String = Id.toString()
+}

+ 127 - 0
app/src/main/java/com/khmer9/yuki/screen/main/MainListViewHolder.kt

@@ -0,0 +1,127 @@
+package com.khmer9.yuki.screen.main
+
+import android.annotation.SuppressLint
+import android.graphics.Color
+import android.os.Build
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import androidx.annotation.RequiresApi
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.extension.setSafeOnClickListener
+import com.khmer9.yuki.remote.CurrentBettingMobile
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import java.text.DecimalFormat
+
+class MainListViewHolder(
+    itemView: View,
+    private val printButtonListener: ((String, String, String, String, String, String) -> Unit)?,
+    private val detailButtonListener: ((String, String) -> Unit)?
+) : BaseListViewHolder(itemView) {
+
+    private val tvId = itemView.findViewById<TextView>(R.id.tv_id)
+    private val tvBet = itemView.findViewById<TextView>(R.id.tv_bet)
+    private val tvAmount = itemView.findViewById<TextView>(R.id.tv_amount)
+    private val tvCode = itemView.findViewById<TextView>(R.id.tv_code)
+    private val tvWinLose = itemView.findViewById<TextView>(R.id.tv_win_lose)
+    private val tvCast = itemView.findViewById<TextView>(R.id.tv_cast)
+    private lateinit var user: User
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @SuppressLint("SetTextI18n")
+    override fun bindView(item: BaseListItem) {
+        Log.d("MainListViewHolder", item.toString())
+        when (item) {
+            is CurrentBettingMobile -> {
+                try {
+                    var datetime = item.time.split(" ")
+                    var time = datetime[1]
+                    var date = datetime[0]
+                    tvId.text = date + System.getProperty("line.separator") + time
+                } catch (e: Exception) {
+                }
+
+                ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+                    user = it.user!!
+                }
+
+                //tvAmount.text = "${currencyFormat(item.totalAmount)}"
+                tvCode.text = "លម្អិត"
+                tvBet.text = "#" + item.no.toString()
+                tvCast.text = "${item.totalCast}"
+
+                try {
+                    if (item.isReleaseResult) {
+                        tvWinLose.visibility = View.VISIBLE
+                        //tvWinLose.text = "${currencyFormat(item.totalAmountWin!!)}"
+                        if (item.totalAmountWin!! > 0) {
+                            tvWinLose.setTextColor(Color.parseColor("#303F9F"))
+                            tvCast.setTextColor(Color.parseColor("#303F9F"))
+                        } else {
+                            tvWinLose.setTextColor(Color.parseColor("#C62828"))
+                            tvCast.setTextColor(Color.parseColor("#C62828"))
+                        }
+                    } else {
+                        tvWinLose.text = "0.0"
+                    }
+                } catch (e: Exception) {
+                    tvWinLose.text = "0.0"
+                }
+
+                when (user.currencyType) {
+                    1 -> {//dollar
+                        tvWinLose.text = "${currencyFormat(item.totalAmountWin!!)} $"
+                        tvAmount.text = "${currencyFormat(item.totalAmount)} $"
+                    }
+                    2 -> {//riel
+                        tvWinLose.text = "${currencyFormat(item.totalAmountWin!!)} ៛"
+                        tvAmount.text = "${currencyFormat(item.totalAmount)} ៛"
+                    }
+                    3 -> {//baht
+                        tvWinLose.text = "${currencyFormat(item.totalAmountWin!!)} ฿"
+                        tvAmount.text = "${currencyFormat(item.totalAmount)} ฿"
+                    }
+                }
+
+                tvCode.setSafeOnClickListener {
+                    it.btnClick().subscribe {
+                        detailButtonListener?.invoke(item.id, item.no.toString())
+                    }
+                }
+
+                try {
+                    val strSplit = item.totalAmount.toString().split("x")
+                    val strSplit2 = strSplit[1].split("=")
+                    val bet = strSplit[0].trim()
+                    val cast = strSplit2[0].trim()
+                    val winLost = bet.toDouble() * cast.toDouble()
+
+                    tvBet.setSafeOnClickListener {
+                        it.btnClick().subscribe {
+                            printButtonListener?.invoke(item.no.toString(), item.id, item.time, item.totalCast.toString(), item.totalAmount.toString(), winLost.toString())
+                        }
+                    }
+                } catch (e: Exception) {
+                    tvBet.setSafeOnClickListener {
+                        it.btnClick().subscribe {
+                            printButtonListener?.invoke(item.no.toString(), item.id, item.time, item.totalCast.toString(), item.totalAmount.toString(), "0")
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 959 - 0
app/src/main/java/com/khmer9/yuki/screen/play/PlayFragment.kt

@@ -0,0 +1,959 @@
+package com.khmer9.yuki.screen.play
+
+import android.annotation.SuppressLint
+import android.media.MediaPlayer
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.widget.AppCompatButton
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import com.ahmedelsayed.sunmiprinterutill.PrintMe
+import com.google.android.material.textview.MaterialTextView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.app.ShareViewState
+import com.khmer9.yuki.base.BaseFragment
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.remote.LottoSet
+import com.khmer9.yuki.remote.SocketMessageDataModel
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.remote.main.SocketClientTimeDataModel
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import com.khmer9.yuki.util.PrefHelper
+import com.khmer9.yuki.view.CircleView
+import com.mazenrashed.printooth.Printooth
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import kotlinx.android.synthetic.main.fragment_play.*
+import kotlinx.android.synthetic.main.view_circle.view.*
+
+/**
+ * A simple [Fragment] subclass as the second destination in the navigation.
+ */
+class PlayFragment : BaseFragment(R.layout.fragment_play) {
+    private lateinit var tvBettingType: TextView
+    private lateinit var tvAmount: TextView
+    private val shareViewModel = App.injectShareViewModel()
+    private lateinit var betTouch: MediaPlayer
+    private lateinit var user: User
+    private lateinit var btnOk: Button
+    private var totalAmount: Int = 0
+    private lateinit var lottoSet: LottoSet
+    private val prefHelper = App.injectPrefHelper()
+    private lateinit var printMe: PrintMe
+    private lateinit var lottoSetList: List<LottoSet>
+    private val lottoSetListBetting: MutableList<LottoSet> = mutableListOf()
+    private lateinit var moshi: Moshi
+
+    private lateinit var putMoney: MediaPlayer
+    private lateinit var betSuccess: MediaPlayer
+
+    private lateinit var sharePref: PrefHelper
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        lottoSetList = ModelPreferencesManager.getList(Const.LOTTO_KEY)!!
+        printMe = PrintMe(context)
+        (requireActivity() as ShareActivity).apply {
+            betTouch = MediaPlayer.create(this, R.raw.bettouch)
+            user = loginData.user!!
+        }
+
+        sharePref = App.injectPrefHelper()
+        putMoney = MediaPlayer.create(requireContext(), R.raw.put_bet_amount)
+        betSuccess = MediaPlayer.create(requireContext(), R.raw.bet_success_2)
+        moshi = Moshi.Builder().build()
+    }
+
+    @SuppressLint("CheckResult")
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        (requireActivity() as? ShareActivity)?.apply {
+            refreshImageView.setOnClickListener {
+                shareViewModel.remoteMainScreenAPI()
+            }
+
+            //TODO: todo later
+            /*shareViewModel.getLottoMessage.observe(viewLifecycleOwner, Observer {
+                getLottoMessage(it)
+            })*/
+
+            shareViewModel.getClientTime.observe(viewLifecycleOwner, Observer {
+                if (!it.isNullOrEmpty()) {
+                    val adapter: JsonAdapter<SocketClientTimeDataModel> = moshi.adapter(
+                        SocketClientTimeDataModel::class.java
+                    )
+                    val socketClientTimeResponse = adapter.fromJson(it)
+                    try {
+                        if (socketClientTimeResponse != null) {
+                            if (socketClientTimeResponse.isCountdown) {
+                                onBackPressed()
+                            }
+                        }
+                    } catch (e: Exception) {
+                        e.printStackTrace()
+                    }
+                }
+            })
+            shareViewModel.getShareState.observe(viewLifecycleOwner, Observer {
+                getPlay(it)
+            })
+            groupToolbarIcons.isVisible = false
+        }
+
+        tvAmount = view.findViewById(R.id.tv_amount_play)
+        tvBettingType = view.findViewById(R.id.tv_game_number_big_small)
+        view.findViewById<AppCompatButton>(R.id.btn_close).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                (requireActivity() as? ShareActivity)?.apply {
+                    onBackPressed()
+                }
+            }
+        }
+
+        btnOk = view.findViewById<Button>(R.id.btn_ok)
+        btnOk.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            //val date = formatShowDate(Date())
+            it.btnClick().subscribe {
+                val minValue = sharePref.getMinBetting()
+                val maxValue = sharePref.getMaxBetting()
+                if (totalAmount in minValue..maxValue) {
+                    try {
+                        if (tvBettingType.text.isNotEmpty()) {
+                            confirmBet(lottoSetListBetting, totalAmount)
+                            /*shareViewModel.remoteCreateBettingAPI(lottoSet, totalAmount)
+                            //Create betting and call the betting current api
+                            //shareViewModel.remoteBettingCurrentMobileAPI()
+                            totalAmount = 0
+                            tvAmount.text = ""
+                            tvBettingType.text = ""*/
+                        } else Toast.makeText(requireContext(), "សូមជ្រើសរើសភ្នាល់", Toast.LENGTH_LONG).show()
+                    } catch (e: java.lang.Exception) {
+                        e.printStackTrace()
+                    }
+                } else {
+                    val message = "ទឹកប្រាក់ភ្នាល់ត្រូវនៅចន្លោះ $minValue ទៅ $maxValue"
+                    putMoney.start()
+                    Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
+                }
+            }
+        }
+
+        shareViewModel.getShareState.observe(viewLifecycleOwner, Observer {
+            printInvoice(it)
+        })
+
+        view.findViewById<ImageView>(R.id.tv_clear).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                totalAmount = 0
+                tvAmount.text = ""
+                tvBettingType.text = ""
+            }
+        }
+
+        //-----------------------------------------------------------------
+        if (user.currencyType == 1) {
+            v_1k.tv_value.text = "1"
+            v_1k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_2k.tv_value.text = "2"
+            v_2k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_5k.tv_value.text = "5"
+            v_5k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_10k.tv_value.text = "10"
+            v_10k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_20k.tv_value.text = "20"
+            v_20k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_50k.tv_value.text = "50"
+            v_50k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_100k.tv_value.text = "100"
+            v_100k.iv_logo.setImageResource(R.drawable.text_dollar)
+
+            v_200k.tv_value.text = "200"
+            v_200k.iv_logo.setImageResource(R.drawable.text_dollar)
+        } else if (user.currencyType == 2) {
+            v_1k.tv_value.text = "1K"
+            v_1k.iv_logo.isVisible = false
+
+            v_2k.tv_value.text = "2K"
+            v_2k.iv_logo.isVisible = false
+
+            v_5k.tv_value.text = "5K"
+            v_5k.iv_logo.isVisible = false
+
+            v_10k.tv_value.text = "10K"
+            v_10k.iv_logo.isVisible = false
+
+            v_20k.tv_value.text = "20K"
+            v_20k.iv_logo.isVisible = false
+
+            v_50k.tv_value.text = "50K"
+            v_50k.iv_logo.isVisible = false
+
+            v_100k.tv_value.text = "100K"
+            v_100k.iv_logo.isVisible = false
+
+            v_200k.tv_value.text = "200K"
+            v_200k.iv_logo.isVisible = false
+        }
+
+        view.findViewById<CircleView>(R.id.v_1k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        1
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 1
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        1000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 1000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+        view.findViewById<CircleView>(R.id.v_2k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        2
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 2
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        2000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 2000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        view.findViewById<CircleView>(R.id.v_5k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        5
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 5
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        5000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 5000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        view.findViewById<CircleView>(R.id.v_10k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        10
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 10
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        10000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 10000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        view.findViewById<CircleView>(R.id.v_20k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        20
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 20
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        20000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 20000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        view.findViewById<CircleView>(R.id.v_50k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        50
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 50
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        50000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 50000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        view.findViewById<CircleView>(R.id.v_100k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        100
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 100
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        100000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 100000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        view.findViewById<CircleView>(R.id.v_200k).setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                if (user.currencyType == 1) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        200
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 200
+                    }
+                } else if (user.currencyType == 2) {
+                    totalAmount = if (tvAmount.text.toString().trim().isBlank()) {
+                        200000
+                    } else {
+                        val amount = tvAmount.text.toString().replace(",", "").toInt()
+                        amount + 200000
+                    }
+                }
+
+                tvAmount.text = currencyFormat(totalAmount)
+            }
+        }
+
+        //-------------------------------------------------------------------------------
+
+        betPair.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                Log.d("betClick", "betPair")
+                //tvBettingType.text = "គូ"
+                lottoSet = findLottoSet(lottoSetList, "គូ")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betPair.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betPair.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_600))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betOdd.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                Log.d("betClick", "betOdd")
+                //tvBettingType.text = "សេស"
+                lottoSet = findLottoSet(lottoSetList, "សេស")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betOdd.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betOdd.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_light_blue))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betBlue.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ខៀវ"
+                lottoSet = findLottoSet(lottoSetList, "ខៀវ")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betBlue.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betBlue.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betRed.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ក្រហម"
+                lottoSet = findLottoSet(lottoSetList, "ក្រហម")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betRed.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betRed.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betOne.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ជូត"
+                lottoSet = findLottoSet(lottoSetList, "ជូត")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betOneTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betOneTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betTwo.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ឆ្លូវ"
+                lottoSet = findLottoSet(lottoSetList, "ឆ្លូវ")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betTwoTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betTwoTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betThree.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ខាល"
+                lottoSet = findLottoSet(lottoSetList, "ខាល")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betThreeTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betThreeTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betFour.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ថោះ"
+                lottoSet = findLottoSet(lottoSetList, "ថោះ")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betFourTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betFourTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betFive.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "រោង"
+                lottoSet = findLottoSet(lottoSetList, "រោង")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betFiveTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betFiveTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betSix.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ម្សាញ់"
+                lottoSet = findLottoSet(lottoSetList, "ម្សាញ់")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betSixTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betSixTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betSeven.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "មមី"
+                lottoSet = findLottoSet(lottoSetList, "មមី")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betSevenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betSevenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betEight.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "មមែ"
+                lottoSet = findLottoSet(lottoSetList, "មមែ")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betEightTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betEightTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betNine.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "វក"
+                lottoSet = findLottoSet(lottoSetList, "វក")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betNineTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betNineTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betTen.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "រកា"
+                lottoSet = findLottoSet(lottoSetList, "រកា")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betTenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betTenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betEleven.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ច"
+                lottoSet = findLottoSet(lottoSetList, "ច")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betElevenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betElevenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betTwelve.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "កុរ"
+                lottoSet = findLottoSet(lottoSetList, "កុរ")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betTwelveTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betTwelveTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betThin.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ស្តើង"
+                lottoSet = findLottoSet(lottoSetList, "ស្តើង")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betThin.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betThin.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_yellow_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        betThick.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "ក្រាស់"
+                lottoSet = findLottoSet(lottoSetList, "ក្រាស់")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    betThick.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    betThick.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_green_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        bet123.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "1-2-3"
+                lottoSet = findLottoSet(lottoSetList, "1-2-3")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    bet123.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    bet123.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        bet456.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "4-5-6"
+                lottoSet = findLottoSet(lottoSetList, "4-5-6")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    bet456.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    bet456.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        bet789.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "7-8-9"
+                lottoSet = findLottoSet(lottoSetList, "7-8-9")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    bet789.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    bet789.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+        bet101112.setOnClickListener {
+            if (!sharePref.getIsMute()) betTouch.start()
+            it.btnClick().subscribe {
+                //tvBettingType.text = "10-11-12"
+                lottoSet = findLottoSet(lottoSetList, "10-11-12")
+                lottoSet.isClick = !lottoSet.isClick
+
+                if (lottoSet.isClick) {
+                    bet101112.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_purple_800))
+                } else {
+                    bet101112.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+                }
+
+                setBettingData(lottoSet)
+            }
+        }
+    }
+
+    private fun currencyType(): String {
+        when (user.currencyType) {
+            1 -> {//dollar
+                return "$"
+            }
+            2 -> {//riel
+                return "៛"
+            }
+            3 -> {//baht
+                return "฿"
+            }
+        }
+        return ""
+    }
+
+    private fun resetBettingUi() {
+        lottoSetListBetting.clear()
+
+        for (item in lottoSetList) {
+            item.isClick = false
+        }
+
+        betPair.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_600))
+        betOdd.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_light_blue))
+        betBlue.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betRed.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betOneTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betTwoTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betThreeTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betFourTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betFiveTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betSixTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betSevenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betEightTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betNineTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        betTenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betElevenTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betTwelveTv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        betThin.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_yellow_800))
+        betThick.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_green_800))
+        bet123.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        bet456.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+        bet789.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_blue_800))
+        bet101112.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.color_red_800))
+    }
+
+    private fun setBettingData(lottoSet: LottoSet) {
+        if (lottoSetListBetting != null) {
+            val item = lottoSetListBetting.contains(lottoSet)
+            if (item) {
+                lottoSetListBetting.remove(lottoSet)
+            } else {
+                lottoSetListBetting += lottoSet
+            }
+        } else {
+            lottoSetListBetting += lottoSet
+        }
+
+        var bettings = ""
+        for (item in lottoSetListBetting) {
+            bettings += item.typeOfBetting + " "
+        }
+        tvBettingType.text = bettings
+    }
+
+    private fun confirmBet(listBettings: MutableList<LottoSet>, amount: Int) {
+        val total = amount * listBettings.count()
+
+        var bettings = ""
+        for (item in lottoSetListBetting) {
+            bettings += item.typeOfBetting + " "
+        }
+
+        val builder = context?.let { AlertDialog.Builder(it) }
+        builder?.setTitle(getString(R.string.confirm_betting))
+        builder?.setMessage(getString(R.string.confirm_betting_2) + " $total ${currencyType()} " + getString(R.string.on) + "$bettings!")
+
+        //performing positive action
+        builder?.setPositiveButton(getString(R.string.yes)) { dialogInterface, which ->
+            shareViewModel.remoteCreateBettingAPI(listBettings, totalAmount)
+            totalAmount = 0
+            tvAmount.text = ""
+            tvBettingType.text = ""
+            dialogInterface.dismiss()
+        }
+        //performing negative action
+        builder?.setNegativeButton(getString(R.string.cancel)) { dialogInterface, which ->
+            dialogInterface.dismiss()
+        }
+        // Create the AlertDialog
+        val alertDialog: AlertDialog? = builder?.create()
+        // Set other dialog properties
+        alertDialog?.setCancelable(false)
+        alertDialog?.show()
+    }
+
+    private fun printInvoice(it: ShareViewState?) {
+        if (it != null) {
+            if (it.isBetSuccess) {
+                betSuccess.start()
+                resetBettingUi()
+
+                val betItem = it.createBettingData
+                Toast.makeText(requireContext(), "ការភ្នាល់របស់អ្នកបានសំរេច!", Toast.LENGTH_SHORT).show()
+                if (prefHelper.getIsAutoPrint()) {
+                    if (Printooth.hasPairedPrinter()) {
+                        if (betItem != null) {
+                            try {
+                                val strSplit = betItem.amount.split("x")
+                                val strSplit2 = strSplit[1].split("=")
+                                val bet = strSplit[0].trim()
+                                val cast = strSplit2[0].trim()
+                                val winLost = bet.toDouble() * cast.toDouble()
+
+                                printSomePrintable(
+                                    user.userName,
+                                    betItem.no.toString(),
+                                    betItem.id,
+                                    betItem.time,
+                                    "",
+                                    betItem.amount,
+                                    winLost.toString()
+                                )
+                            } catch (e: Exception) {
+                                printSomePrintable(
+                                    user.userName,
+                                    betItem.no.toString(),
+                                    betItem.id,
+                                    betItem.time,
+                                    "",
+                                    betItem.amount,
+                                    "0"
+                                )
+                            }
+                        }
+                    } else {
+                        val views = layoutInflater.inflate(R.layout.invoice_print, null)
+                        views.findViewById<MaterialTextView>(R.id.title).text = user.userName
+                        if (betItem != null) {
+                            views.findViewById<MaterialTextView>(R.id.ticketId).text = betItem.no.toString()
+                            views.findViewById<MaterialTextView>(R.id.gameId).text = betItem.id
+                            views.findViewById<MaterialTextView>(R.id.date).text = betItem.time
+                            views.findViewById<MaterialTextView>(R.id.betType).text = ""
+                            views.findViewById<MaterialTextView>(R.id.betPoint).text = betItem.amount
+
+                            try {
+                                Log.d("MainListViewHolder2", betItem.amount)
+                                val strSplit = betItem.amount.split("x")
+                                val strSplit2 = strSplit[1].split(":")
+                                val bet = strSplit[0].trim()
+                                val cast = strSplit2[0].trim()
+                                val winLost = bet.toDouble() * cast.toDouble()
+                                views.findViewById<MaterialTextView>(R.id.winLose).text = winLost.toString()
+                            } catch (e: Exception) {
+                                views.findViewById<MaterialTextView>(R.id.winLose).text = "0"
+                            }
+
+                            printMe.sendViewToPrinter(views)
+                        }
+                    }
+                }
+
+                it.isBetSuccess = false
+            } else {
+                if (it.betError != null) Toast.makeText(requireContext(), "ការភ្នាល់មិនបានសំរេច!", Toast.LENGTH_SHORT).show()
+            }
+        }
+    }
+
+    private fun getPlay(state: ShareViewState) {
+        if (state.accountBalance != null) {
+            (requireActivity() as? ShareActivity)?.apply {
+                balanceTextView.text = currencyFormat(state.accountBalance.toDouble())
+            }
+        }
+    }
+
+    @SuppressLint("SetTextI18n")
+    fun getLottoMessage(loto: SocketMessageDataModel) {
+        if (loto.isCountDowning() && loto.countDown!! <= 10) {
+            (requireActivity() as? ShareActivity)?.apply {
+                onBackPressed()
+            }
+        }
+
+        if (!loto.isCountDowning()) {
+            (requireActivity() as? ShareActivity)?.apply {
+                onBackPressed()
+            }
+        }
+
+        when (loto.countDown) {
+            in 15 downTo 11 -> {
+                btnOk.setText("OK(${loto.countDown!! - 10})")
+            }
+        }
+    }
+}

+ 50 - 0
app/src/main/java/com/khmer9/yuki/screen/report/ReportAdapter.kt

@@ -0,0 +1,50 @@
+package com.khmer9.yuki.screen.report
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+
+class ReportAdapter(private val type: Type) : BaseListAdapter() {
+    var printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+        when (type) {
+            Type.WIN -> {
+                return ReportListViewHolder(
+                    LayoutInflater.from(parent.context).inflate(
+                        R.layout.item_table_holder,
+                        parent,
+                        false
+                    ), printButtonListener
+                )
+            }
+
+            Type.WIN_LOST -> {
+                return ReportListViewHolder(
+                    LayoutInflater.from(parent.context).inflate(
+                        R.layout.item_table_holder,
+                        parent,
+                        false
+                    ), printButtonListener
+                )
+            }
+
+            Type.STATEMENT -> {//Statement
+                return ReportListViewHolder(
+                    LayoutInflater.from(parent.context).inflate(
+                        R.layout.item_report,
+                        parent,
+                        false
+                    )
+                )
+            }
+
+        }
+    }
+}
+
+enum class Type {
+    WIN, WIN_LOST, STATEMENT
+}

+ 36 - 0
app/src/main/java/com/khmer9/yuki/screen/report/ReportListViewHolder.kt

@@ -0,0 +1,36 @@
+package com.khmer9.yuki.screen.report
+
+import android.annotation.SuppressLint
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+import com.khmer9.yuki.remote.StatementBetting
+import java.text.DecimalFormat
+
+
+class ReportListViewHolder(itemView: View, private val printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null) : BaseListViewHolder(itemView) {
+
+    @SuppressLint("SetTextI18n")
+    override fun bindView(item: BaseListItem) {
+
+        when (item) {
+            is StatementBetting -> {//Statement
+                val imgC1 = itemView.findViewById<ImageView>(R.id.img_c1)
+                val tvC2 = itemView.findViewById<TextView>(R.id.tv_c2)
+                val tvC3 = itemView.findViewById<TextView>(R.id.tv_c3)
+                val tvC4 = itemView.findViewById<TextView>(R.id.tv_c4)
+                // val tvC5 = itemView.findViewById<TextView>(R.id.tv_c5)
+                tvC2.text = item.date
+                tvC3.text = currencyFormat(item.amount!!.toInt())
+            }
+        }
+    }
+
+    fun currencyFormat(amount: Int): String? {
+        val formatter = DecimalFormat("#,###")
+        return formatter.format(amount)
+    }
+}

+ 23 - 0
app/src/main/java/com/khmer9/yuki/screen/report/ReportViewState.kt

@@ -0,0 +1,23 @@
+package com.khmer9.yuki.screen.report
+
+import com.khmer9.yuki.remote.StatementBetting
+import com.khmer9.yuki.remote.StatementDeposit
+import com.khmer9.yuki.remote.StatementResult
+import com.khmer9.yuki.remote.StatementWithdraw
+
+data class ReportViewState(
+    val initial: Boolean = true,
+    val isProgress: Boolean = false,
+    val error: String? = null,
+    var statementList: List<StatementBetting>? = null,
+    var statementListTurnOver: String? = null,
+    var statementListAmountWinLose: String? = null,
+
+    var statementWithdrawList: List<StatementWithdraw>? = null,
+    var statementWithdrawTotalAmount: String? = null,
+
+    var statementDepositList: List<StatementDeposit>? = null,
+    var statementDepositTotalAmount: String? = null,
+
+    var statementResultList: List<StatementResult>? = null
+)

+ 245 - 0
app/src/main/java/com/khmer9/yuki/screen/report/StatementFragment.kt

@@ -0,0 +1,245 @@
+package com.khmer9.yuki.screen.report
+
+import android.annotation.SuppressLint
+import android.media.MediaPlayer
+import android.os.Bundle
+import android.view.View
+import android.widget.ImageView
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.ConcatAdapter
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.khmer9.yuki.R
+import com.khmer9.yuki.adapter.*
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.base.BaseFragment
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.remote.main.SocketClientTimeDataModel
+import com.khmer9.yuki.screen.detail.BettingDetailFragment
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import kotlinx.android.synthetic.main.fragment_report.*
+
+/**
+ * A simple [Fragment] subclass as the second destination in the navigation.
+ */
+class StatementFragment : BaseFragment(R.layout.fragment_report) {
+    private val shareViewModel = App.injectShareViewModel()
+    lateinit var adapterTb1: BettingStatementAdapter
+    lateinit var adapterTb1Header: BettingStatementHeaderAdapter
+    lateinit var adapterTb2: WithdrawStatementAdapter
+    lateinit var adapterTb2Header: WithdrawStatementHeaderAdapter
+    lateinit var adapterTb3: DepositStatementAdapter
+    lateinit var adapterTb3Header: DepositStatementHeaderAdapter
+    lateinit var adapterTb4: ResultStatementAdapter
+    lateinit var adapterTb4Header: ResultStatementHeaderAdapter
+    lateinit var rvTab1: RecyclerView //betting statement
+    lateinit var user: User
+    lateinit var rvTab2: RecyclerView //deposit statement
+    lateinit var rvTab3: RecyclerView //withdraw statement
+    lateinit var rvTab4: RecyclerView //result statement
+
+    private lateinit var betTouch: MediaPlayer
+    private lateinit var moshi: Moshi
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        (requireActivity() as ShareActivity).apply {
+            betTouch = MediaPlayer.create(this, R.raw.bettouch)
+            //user = userShare
+            user = loginData.user!!
+        }
+        moshi = Moshi.Builder().build()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        rvTab1 = view.findViewById(R.id.rv_tb1)
+        rvTab2 = view.findViewById(R.id.rv_tb2)
+        rvTab3 = view.findViewById(R.id.rv_tb3)
+        rvTab4 = view.findViewById(R.id.rv_tb4)
+
+        (requireActivity() as? ShareActivity)?.apply {
+
+            groupToolbarIcons.isVisible = false
+
+            view.findViewById<ImageView>(R.id.btn_close).setOnClickListener {
+                if (!sharePref.getIsMute())
+                    betTouch.start()
+
+                it.btnClick().subscribe {
+                    onBackPressed()
+                }
+            }
+
+            refreshImageView.setOnClickListener {
+                shareViewModel.remoteReportBettingStatement()
+                shareViewModel.remoteReportWithdrawStatement()
+                shareViewModel.remoteReportDepositStatement()
+                shareViewModel.remoteReportResultStatement()
+            }
+        }
+
+        //load data...
+        shareViewModel.remoteReportBettingStatement()
+        shareViewModel.remoteReportWithdrawStatement()
+        shareViewModel.remoteReportDepositStatement()
+        shareViewModel.remoteReportResultStatement()
+
+        shareViewModel.getReportState.observe(viewLifecycleOwner, Observer {
+            getReport(it)
+        })
+
+        shareViewModel.getClientTime.observe(viewLifecycleOwner, Observer {
+            if (!it.isNullOrEmpty()) {
+                val adapter: JsonAdapter<SocketClientTimeDataModel> = moshi.adapter(
+                    SocketClientTimeDataModel::class.java
+                )
+                val socketClientTimeResponse = adapter.fromJson(it)
+                if (socketClientTimeResponse != null) {
+                    getMessage(socketClientTimeResponse)
+                }
+            }
+        })
+
+        v_tab1.setOnClickListener {
+            bg_select_tab1.visibility = View.VISIBLE
+            bg_select_tab2.visibility = View.INVISIBLE
+            bg_select_tab3.visibility = View.INVISIBLE
+            bg_select_tab4.visibility = View.INVISIBLE
+
+            rvTab1.visibility = View.VISIBLE
+            rvTab2.visibility = View.INVISIBLE
+            tab3.visibility = View.INVISIBLE
+            rvTab4.visibility = View.INVISIBLE
+
+            shareViewModel.remoteReportBettingStatement()
+        }
+
+        v_tab2.setOnClickListener {
+            bg_select_tab1.visibility = View.INVISIBLE
+            bg_select_tab2.visibility = View.VISIBLE
+            bg_select_tab3.visibility = View.INVISIBLE
+            bg_select_tab4.visibility = View.INVISIBLE
+
+            rvTab1.visibility = View.INVISIBLE
+            rvTab2.visibility = View.VISIBLE
+            tab3.visibility = View.INVISIBLE
+            rvTab4.visibility = View.INVISIBLE
+
+            shareViewModel.remoteReportDepositStatement()
+        }
+
+        v_tab3.setOnClickListener {
+            bg_select_tab1.visibility = View.INVISIBLE
+            bg_select_tab2.visibility = View.INVISIBLE
+            bg_select_tab3.visibility = View.VISIBLE
+            bg_select_tab4.visibility = View.INVISIBLE
+
+            rvTab1.visibility = View.INVISIBLE
+            rvTab2.visibility = View.INVISIBLE
+            tab3.visibility = View.VISIBLE
+            rvTab4.visibility = View.INVISIBLE
+
+            shareViewModel.remoteReportWithdrawStatement()
+        }
+
+        v_tab4.setOnClickListener {
+            bg_select_tab1.visibility = View.INVISIBLE
+            bg_select_tab2.visibility = View.INVISIBLE
+            bg_select_tab3.visibility = View.INVISIBLE
+            bg_select_tab4.visibility = View.VISIBLE
+
+            rvTab1.visibility = View.INVISIBLE
+            rvTab2.visibility = View.INVISIBLE
+            tab3.visibility = View.INVISIBLE
+            rvTab4.visibility = View.VISIBLE
+
+            shareViewModel.remoteReportResultStatement()
+        }
+    }
+
+    private fun getReport(state: ReportViewState) {
+        progress.isVisible = state.isProgress
+
+        if (state.statementList != null) {
+            adapterTb1 = BettingStatementAdapter()
+            adapterTb1Header = BettingStatementHeaderAdapter(state.statementListTurnOver, state.statementListAmountWinLose)
+
+            adapterTb1.submitList(state.statementList)
+            adapterTb1.detailButtonListener = { invoiceId, invoiceNo ->
+                if (!invoiceId.isNullOrEmpty()) {
+                    (requireActivity() as? ShareActivity)?.apply {
+                        supportFragmentManager.beginTransaction()
+                            .setCustomAnimations(
+                                R.anim.slide_in,
+                                R.anim.slide_out,
+                                R.anim.slide_in_exit,
+                                R.anim.slide_out_exit
+                            )
+                            .add(R.id.container, BettingDetailFragment().apply {
+                                arguments = Bundle().apply {
+                                    putString("INVOICE_ID", invoiceId)
+                                    putString("no", invoiceNo)
+                                }
+                            })
+                            .addToBackStack(null)
+                            .commit()
+                    }
+                }
+            }
+
+            rvTab1.setHasFixedSize(true)
+            rvTab1.layoutManager = LinearLayoutManager(context)
+            val concatAdapter = ConcatAdapter(adapterTb1Header, adapterTb1)
+            rvTab1.adapter = concatAdapter
+        }
+
+        if (state.statementWithdrawList != null) {
+            adapterTb2 = WithdrawStatementAdapter()
+            adapterTb2Header = WithdrawStatementHeaderAdapter(state.statementWithdrawTotalAmount)
+
+            adapterTb2.submitList(state.statementWithdrawList)
+            rvTab2.setHasFixedSize(true)
+            rvTab2.layoutManager = LinearLayoutManager(context)
+            val concatAdapter = ConcatAdapter(adapterTb2Header, adapterTb2)
+            rvTab2.adapter = concatAdapter
+        }
+
+        if (state.statementDepositList != null) {
+            adapterTb3 = DepositStatementAdapter()
+            adapterTb3Header = DepositStatementHeaderAdapter(state.statementDepositTotalAmount) //WithdrawStatementHeaderAdapter(state.statementDepositTotalAmount)
+
+            adapterTb3.submitList(state.statementDepositList)
+            rvTab3.setHasFixedSize(true)
+            rvTab3.layoutManager = LinearLayoutManager(context)
+            val concatAdapter = ConcatAdapter(adapterTb3Header, adapterTb3)
+            rvTab3.adapter = concatAdapter
+        }
+
+        if (state.statementResultList != null) {
+            adapterTb4 = ResultStatementAdapter()
+            adapterTb4Header = ResultStatementHeaderAdapter()
+
+            adapterTb4.submitList(state.statementResultList)
+            rvTab4.setHasFixedSize(true)
+            rvTab4.layoutManager = LinearLayoutManager(context)
+            val concatAdapter = ConcatAdapter(adapterTb4Header, adapterTb4)
+            rvTab4.adapter = concatAdapter
+        }
+    }
+
+    @SuppressLint("SetTextI18n")
+    fun getMessage(lotto: SocketClientTimeDataModel) {
+        if (lotto.isCountDowning() && lotto.time == 128) {
+            (requireActivity() as? ShareActivity)?.apply {
+                //shareViewModel.remoteReport()
+            }
+        }
+    }
+}

+ 14 - 0
app/src/main/java/com/khmer9/yuki/screen/report/bettingStatement/BettingHeaderViewHolder.kt

@@ -0,0 +1,14 @@
+package com.khmer9.yuki.screen.report.bettingStatement
+
+import android.view.View
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.remote.StatementBetting
+
+class BettingHeaderViewHolder(itemView: View, private val printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null) : BaseListViewHolder(itemView) {
+    override fun bindView(item: BaseListItem) {
+        if (item is StatementBetting) {
+
+        }
+    }
+}

+ 92 - 0
app/src/main/java/com/khmer9/yuki/screen/report/bettingStatement/ReportBettingViewHolder.kt

@@ -0,0 +1,92 @@
+package com.khmer9.yuki.screen.report.bettingStatement
+
+import android.annotation.SuppressLint
+import android.graphics.Color
+import android.graphics.Typeface
+import android.view.View
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.extension.setSafeOnClickListener
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.remote.StatementBetting
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import java.text.DecimalFormat
+
+class ReportBettingViewHolder(
+    itemView: View,
+    private val printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null,
+    private val detailButtonListener: ((String, String) -> Unit)?
+) : BaseListViewHolder(itemView) {
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    @SuppressLint("SetTextI18n")
+    override fun bindView(item: BaseListItem) {
+        val tvId = itemView.findViewById<TextView>(R.id.tv_id)
+        val tvBetType = itemView.findViewById<TextView>(R.id.tv_bet_type)
+        val tvBetAmount = itemView.findViewById<TextView>(R.id.tv_bet_amount)
+        val tvPayout = itemView.findViewById<TextView>(R.id.tv_payout)
+        val tvWinLost = itemView.findViewById<TextView>(R.id.tv_win_lose)
+        val tvCast = itemView.findViewById<TextView>(R.id.tv_cast)
+        lateinit var user: User
+
+        ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+            user = it.user!!
+        }
+
+        if (item is StatementBetting) {
+            tvId.text = "#" + item.no
+
+            var datetime = item.date.split(" ")
+            var time = datetime[1]
+            var date = datetime[0]
+            tvPayout.text = date + System.getProperty("line.separator") + time
+
+            if (item.winLose > 0) {
+                tvBetAmount.setTextColor(Color.parseColor("#303F9F"))
+                tvBetAmount.setTypeface(null, Typeface.BOLD)
+
+                tvCast.setTextColor(Color.parseColor("#303F9F"))
+                tvCast.setTypeface(null, Typeface.BOLD)
+            } else {
+                tvBetAmount.setTextColor(Color.parseColor("#C62828"))
+                tvBetAmount.setTypeface(null, Typeface.BOLD)
+
+                tvCast.setTextColor(Color.parseColor("#C62828"))
+                tvCast.setTypeface(null, Typeface.BOLD)
+            }
+
+            tvWinLost.text = "លម្អិត"
+            tvWinLost.setSafeOnClickListener {
+                it.btnClick().subscribe {
+                    detailButtonListener?.invoke(item.id, item.no.toString())
+                }
+            }
+
+            tvCast.text = "${item.cast}"
+
+            when (user.currencyType) {
+                1 -> {//dollar
+                    tvBetType.text = "${currencyFormat(item.amount!!)} $"
+                    tvBetAmount.text = "${currencyFormat(item.winLose)} $"
+                }
+                2 -> {//riel
+                    tvBetType.text = "${currencyFormat(item.amount!!)} ៛"
+                    tvBetAmount.text = "${currencyFormat(item.winLose)} ៛"
+                }
+                3 -> {//baht
+                    tvBetType.text = "${currencyFormat(item.amount!!)} ฿"
+                    tvBetAmount.text = "${currencyFormat(item.winLose)} ฿"
+                }
+            }
+        }
+    }
+}

+ 94 - 0
app/src/main/java/com/khmer9/yuki/screen/report/depositStatement/ReportDepositViewHolder.kt

@@ -0,0 +1,94 @@
+package com.khmer9.yuki.screen.report.depositStatement
+
+import android.annotation.SuppressLint
+import android.graphics.Color
+import android.graphics.Typeface
+import android.view.View
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.remote.StatementDeposit
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import java.text.DecimalFormat
+import java.text.ParseException
+import java.text.SimpleDateFormat
+
+class ReportDepositViewHolder(itemView: View, private val printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null) : BaseListViewHolder(itemView) {
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    @SuppressLint("SetTextI18n")
+    override fun bindView(item: BaseListItem) {
+        val tvId = itemView.findViewById<TextView>(R.id.tv_id)
+        val tvDate = itemView.findViewById<TextView>(R.id.tv_date)
+        val tvOldBalance = itemView.findViewById<TextView>(R.id.tv_old_balance)
+        val tvNewBalance = itemView.findViewById<TextView>(R.id.tv_new_balance)
+        val tvAmount = itemView.findViewById<TextView>(R.id.tv_amount)
+        lateinit var user: User
+
+        if (item is StatementDeposit) {
+            ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+                user = it.user!!
+            }
+
+            tvId.text = item.id
+
+            val df = SimpleDateFormat("yyyy-MM-dd")
+            val parser = SimpleDateFormat("yyyy-MM-dd hh:mm a")
+            try {
+                val date = df.format(parser.parse(item.date))
+                tvDate.text = date.toString()
+            } catch (e: ParseException) {
+                tvDate.text = item.date
+            }
+
+            /*tvOldBalance.text = item.oldBalance.toString()
+            tvOldBalance.setTextColor(Color.rgb(194, 122, 0))
+            tvOldBalance.setTypeface(null, Typeface.BOLD)
+
+            tvNewBalance.text = item.newBalance.toString()
+            tvNewBalance.setTextColor(Color.BLUE)
+            tvNewBalance.setTypeface(null, Typeface.BOLD)
+
+            tvAmount.text = item.amount.toString()
+            tvAmount.setTextColor(Color.RED)
+            tvAmount.setTypeface(null, Typeface.BOLD)*/
+
+            when (user.currencyType) {
+                1 -> {//dollar
+                    tvOldBalance.text = "${currencyFormat(item.oldBalance)} $"
+                    tvNewBalance.text = "${currencyFormat(item.newBalance)} $"
+                    tvAmount.text = "${currencyFormat(item.amount)} $"
+                }
+                2 -> {//riel
+                    tvOldBalance.text = "${currencyFormat(item.oldBalance)} ៛"
+                    tvNewBalance.text = "${currencyFormat(item.newBalance)} ฿"
+                    tvAmount.text = "${currencyFormat(item.amount)} ฿"
+                }
+                3 -> {//baht
+                    tvOldBalance.text = "${currencyFormat(item.oldBalance)} ฿"
+                    tvNewBalance.text = "${currencyFormat(item.newBalance)} ฿"
+                    tvAmount.text = "${currencyFormat(item.amount)} ฿"
+                }
+            }
+
+            tvOldBalance.setTextColor(Color.parseColor("#2E7D32"))
+            tvOldBalance.setTypeface(null, Typeface.BOLD)
+
+            //tvNewBalance.text = item.newBalance.toString()
+            tvNewBalance.setTextColor(Color.parseColor("#303F9F"))
+            tvNewBalance.setTypeface(null, Typeface.BOLD)
+
+            //tvAmount.text = item.amount.toString()
+            tvAmount.setTextColor(Color.parseColor("#F9A825"))
+            tvAmount.setTypeface(null, Typeface.BOLD)
+        }
+    }
+}

+ 63 - 0
app/src/main/java/com/khmer9/yuki/screen/report/resultStatement/ReportResultViewHolder.kt

@@ -0,0 +1,63 @@
+package com.khmer9.yuki.screen.report.resultStatement
+
+import android.annotation.SuppressLint
+import android.graphics.Color
+import android.graphics.Typeface
+import android.view.View
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+import com.khmer9.yuki.remote.StatementResult
+
+class ReportResultViewHolder(itemView: View, private val printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null) : BaseListViewHolder(itemView) {
+    @SuppressLint("SetTextI18n")
+    override fun bindView(item: BaseListItem) {
+        val tvId = itemView.findViewById<TextView>(R.id.tv_id)
+        val tvResult = itemView.findViewById<TextView>(R.id.tv_result)
+        val tvOverUnder = itemView.findViewById<TextView>(R.id.tv_over_under)
+        val tvRange = itemView.findViewById<TextView>(R.id.tv_range)
+
+        if (item is StatementResult) {
+            try {
+                val gameNo = item.date.split(" ")[0] + "#" + item.no
+                tvId.text = gameNo
+            } catch (err: Exception) {
+                tvId.text = item.no.toString()
+            }
+
+            //Result and Range
+            tvResult.text = item.resultYear.name
+            tvRange.text = item.resultRange.name
+            tvOverUnder.text = item.smallLarge.name
+
+            tvResult.setTypeface(null, Typeface.BOLD)
+            tvRange.setTypeface(null, Typeface.BOLD)
+            tvOverUnder.setTypeface(null, Typeface.BOLD)
+
+            when (item.resultRange.name) {
+                "1-2-3" -> {
+                    tvRange.setTextColor(Color.BLUE)
+                    tvResult.setTextColor(Color.BLUE)
+                    tvOverUnder.setTextColor(Color.BLUE)
+                }
+                "4-5-6" -> {
+                    tvRange.setTextColor(Color.BLUE)
+                    tvResult.setTextColor(Color.BLUE)
+                    tvOverUnder.setTextColor(Color.BLUE)
+                }
+                "7-8-9" -> {
+                    tvRange.setTextColor(Color.RED)
+                    tvResult.setTextColor(Color.RED)
+                    tvOverUnder.setTextColor(Color.RED)
+                }
+                "10-11-12" -> {
+                    tvRange.setTextColor(Color.RED)
+                    tvResult.setTextColor(Color.RED)
+                    tvOverUnder.setTextColor(Color.RED)
+                }
+            }
+
+        }
+    }
+}

+ 82 - 0
app/src/main/java/com/khmer9/yuki/screen/report/withdrawStatement/ReportWithdrawViewHolder.kt

@@ -0,0 +1,82 @@
+package com.khmer9.yuki.screen.report.withdrawStatement
+
+import android.annotation.SuppressLint
+import android.graphics.Color
+import android.graphics.Typeface
+import android.view.View
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+import com.khmer9.yuki.remote.LoginData
+import com.khmer9.yuki.remote.StatementWithdraw
+import com.khmer9.yuki.remote.User
+import com.khmer9.yuki.util.Const
+import com.khmer9.yuki.util.ModelPreferencesManager
+import java.text.DecimalFormat
+import java.text.ParseException
+import java.text.SimpleDateFormat
+
+class ReportWithdrawViewHolder(itemView: View, private val printButtonListener: ((String, String, String, String, String, String) -> Unit)? = null) : BaseListViewHolder(itemView) {
+
+    fun currencyFormat(amount: Double): String? {
+        val formatter = DecimalFormat("#,###.##")
+        return formatter.format(amount)
+    }
+
+    @SuppressLint("SetTextI18n")
+    override fun bindView(item: BaseListItem) {
+        val tvId = itemView.findViewById<TextView>(R.id.tv_id)
+        val tvDate = itemView.findViewById<TextView>(R.id.tv_date)
+        val tvOldBalance = itemView.findViewById<TextView>(R.id.tv_old_balance)
+        val tvNewBalance = itemView.findViewById<TextView>(R.id.tv_new_balance)
+        val tvAmount = itemView.findViewById<TextView>(R.id.tv_amount)
+        lateinit var user: User
+
+        if (item is StatementWithdraw) {
+            ModelPreferencesManager.get<LoginData>(Const.USER_KEY)?.let {
+                user = it.user!!
+            }
+
+            tvId.text = item.id
+
+            val df = SimpleDateFormat("yyyy-MM-dd")
+            val parser = SimpleDateFormat("yyyy-MM-dd hh:mm a")
+            try {
+                val date = df.format(parser.parse(item.date))
+                tvDate.text = date.toString()
+            } catch (e: ParseException) {
+                tvDate.text = item.date
+            }
+
+            when (user.currencyType) {
+                1 -> {//dollar
+                    tvOldBalance.text = "${currencyFormat(item.oldBalance)} $"
+                    tvNewBalance.text = "${currencyFormat(item.newBalance)} $"
+                    tvAmount.text = "${currencyFormat(item.amount)} $"
+                }
+                2 -> {//riel
+                    tvOldBalance.text = "${currencyFormat(item.oldBalance)} ៛"
+                    tvNewBalance.text = "${currencyFormat(item.newBalance)} ฿"
+                    tvAmount.text = "${currencyFormat(item.amount)} ฿"
+                }
+                3 -> {//baht
+                    tvOldBalance.text = "${currencyFormat(item.oldBalance)} ฿"
+                    tvNewBalance.text = "${currencyFormat(item.newBalance)} ฿"
+                    tvAmount.text = "${currencyFormat(item.amount)} ฿"
+                }
+            }
+
+            tvOldBalance.setTextColor(Color.parseColor("#2E7D32"))
+            tvOldBalance.setTypeface(null, Typeface.BOLD)
+
+            //tvNewBalance.text = item.newBalance.toString()
+            tvNewBalance.setTextColor(Color.parseColor("#303F9F"))
+            tvNewBalance.setTypeface(null, Typeface.BOLD)
+
+            //tvAmount.text = item.amount.toString()
+            tvAmount.setTextColor(Color.parseColor("#F9A825"))
+            tvAmount.setTypeface(null, Typeface.BOLD)
+        }
+    }
+}

+ 422 - 0
app/src/main/java/com/khmer9/yuki/screen/result/ResultFragment.kt

@@ -0,0 +1,422 @@
+package com.khmer9.yuki.screen.result
+
+import android.annotation.SuppressLint
+import android.media.MediaPlayer
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.GridLayoutManager
+import com.gdtlib.lib.adapter.BaseListItem
+import com.khmer9.yuki.R
+import com.khmer9.yuki.app.App
+import com.khmer9.yuki.app.ShareActivity
+import com.khmer9.yuki.base.BaseFragment
+import com.khmer9.yuki.extension.btnClick
+import com.khmer9.yuki.remote.ResultLotto
+import com.khmer9.yuki.remote.main.SocketClientResultDataModel
+import com.khmer9.yuki.remote.main.SocketClientTimeDataModel
+import com.khmer9.yuki.util.MiddleDividerItemDecoration
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import kotlinx.android.synthetic.main.fragment_result.*
+
+/**
+ * A simple [Fragment] subclass as the second destination in the navigation.
+ */
+class ResultFragment : BaseFragment(R.layout.fragment_result) {
+    private lateinit var adapterTable0: Table0Adapter
+    private lateinit var adapterTable1: Table1Adapter
+    private lateinit var adapterTable2: Table2Adapter
+    private lateinit var adapterTable3: Table3Adapter
+    private lateinit var adapterTable4: Table4Adapter
+    private val shareViewModel = App.injectShareViewModel()
+    private lateinit var itemDecoration: MiddleDividerItemDecoration
+    private lateinit var betTouch: MediaPlayer
+    private lateinit var moshi: Moshi
+    private var itemsTable4: MutableList<Table4ListItem> = mutableListOf()
+    private var itemsTable3: MutableList<Table3ListItem> = mutableListOf()
+    private var itemsTable2: MutableList<Table2ListItem> = mutableListOf()
+    private val itemsTable1: MutableList<Table1ListItem> = mutableListOf()
+    private val itemsTable0: MutableList<Table0ListItem> = mutableListOf()
+    private var resultList : MutableList<ResultLotto> = mutableListOf()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        (requireActivity() as ShareActivity).apply {
+            betTouch = MediaPlayer.create(this, R.raw.bettouch)
+        }
+        moshi = Moshi.Builder().build()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        (requireActivity() as? ShareActivity)?.apply {
+            groupToolbarIcons.isVisible = false
+            view.findViewById<ImageView>(R.id.btn_close).setOnClickListener {
+                if (!sharePref.getIsMute()) betTouch.start()
+                it.btnClick().subscribe {
+                    onBackPressed()
+                }
+            }
+
+            refreshImageView.setOnClickListener {
+                shareViewModel.remoteResult()
+            }
+
+            shareViewModel.getClientTime.observe(viewLifecycleOwner, Observer {
+                if (!it.isNullOrEmpty()) {
+                    val adapter: JsonAdapter<SocketClientTimeDataModel> = moshi.adapter(
+                        SocketClientTimeDataModel::class.java
+                    )
+                    val socketClientTimeResponse = adapter.fromJson(it)
+                    if (socketClientTimeResponse != null) {
+                        getMessage(socketClientTimeResponse)
+                    }
+                }
+            })
+        }
+
+        itemDecoration = MiddleDividerItemDecoration(requireContext(), MiddleDividerItemDecoration.ALL)
+        itemDecoration.setDividerColor(
+            ContextCompat.getColor(
+                requireContext(),
+                R.color.color_gray_800
+            )
+        )
+        shareViewModel.remoteResult()
+        shareViewModel.getResultState.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
+            getResult(it)
+        })
+        shareViewModel.getClientResult.observe(viewLifecycleOwner, Observer {
+            shareViewModel.remoteResult()
+        })
+        adapterTable0 = Table0Adapter()
+        for (n in 1..120) {
+            itemsTable0?.add(Table0ListItem(n, 0))
+        }
+        rv_tb0.addItemDecoration(itemDecoration)
+        rv_tb0.setHasFixedSize(true)
+        rv_tb0.layoutManager = GridLayoutManager(requireContext(), 20)
+        rv_tb0.adapter = adapterTable0
+        adapterTable0.submitList(itemsTable0 as List<BaseListItem>?)
+
+        adapterTable1 = Table1Adapter()
+        for (n in 1..120) {
+            itemsTable1?.add(Table1ListItem(n, 0))
+        }
+        rv_tb1.addItemDecoration(itemDecoration)
+        rv_tb1.setHasFixedSize(true)
+        rv_tb1.layoutManager = GridLayoutManager(requireContext(), 20)
+        rv_tb1.adapter = adapterTable1
+        adapterTable1.submitList(itemsTable1 as List<BaseListItem>?)
+
+        adapterTable2 = Table2Adapter()
+        for (n in 1..120) {
+            itemsTable2?.add(Table2ListItem(n, 0))
+        }
+        rv_tb2.addItemDecoration(itemDecoration)
+        rv_tb2.setHasFixedSize(true)
+        rv_tb2.layoutManager = GridLayoutManager(requireContext(), 20)
+        rv_tb2.adapter = adapterTable2
+        adapterTable2.submitList(itemsTable2 as List<Table2ListItem>)
+
+        adapterTable3 = Table3Adapter()
+        for (n in 1..120) {
+            itemsTable3?.add(Table3ListItem(n, 0))
+        }
+        rv_tb3.addItemDecoration(itemDecoration)
+        rv_tb3.setHasFixedSize(true)
+        rv_tb3.layoutManager = GridLayoutManager(requireContext(), 20)
+        rv_tb3.adapter = adapterTable3
+        adapterTable3.submitList(itemsTable3 as List<Table3ListItem>)
+
+        adapterTable4 = Table4Adapter()
+        for (n in 1..120) {
+            itemsTable4?.add(Table4ListItem(n, 0))
+        }
+        rv_tb4.addItemDecoration(itemDecoration)
+        rv_tb4.setHasFixedSize(true)
+        rv_tb4.layoutManager = GridLayoutManager(requireContext(), 20)
+        rv_tb4.adapter = adapterTable4
+        adapterTable4.submitList(itemsTable4 as List<Table4ListItem>)
+    }
+
+    private fun getResult(state: ResultViewState) {
+        if (state.isProgress) {
+            progress_tb0.isVisible = true
+            progress_tb1.isVisible = true
+            progress_tb2.isVisible = true
+            progress_tb3.isVisible = true
+            progress_tb4.isVisible = true
+        } else {
+            progress_tb0.isVisible = false
+            progress_tb1.isVisible = false
+            progress_tb2.isVisible = false
+            progress_tb3.isVisible = false
+            progress_tb4.isVisible = false
+        }
+        if (!state.resultList.isNullOrEmpty()) {
+            Log.d("resultList", ""+ state.resultList?.size)
+            //1 row have 20 column
+            resultList = state.resultList as MutableList<ResultLotto>
+            var yearIndex = 0
+            var nDuplicateYear = 0
+            val listYear = arrayListOf<Table0ListItem>()
+            for (n in 1..120) {
+                listYear.add(Table0ListItem(n, 0))
+            }
+
+            var bigSmallIndex = 0
+            var nDuplicateBigSmall = 0
+            val listBigSmall = arrayListOf<Table1ListItem>()
+            for (n in 1..120) {
+                listBigSmall.add(Table1ListItem(n, 0))
+            }
+
+            val listRange = arrayListOf<Table2ListItem>()
+            for (n in 1..120) {
+                listRange.add(Table2ListItem(n, 0))
+            }
+            var rangeIndex = 0
+            var nDuplicateRange = 0
+
+            var oddEvenIndex = 0
+            var nDuplicateOddEven = 0
+            val listOddEven = arrayListOf<Table3ListItem>()
+            for (n in 1..120) {
+                listOddEven.add(Table3ListItem(n, 0))
+            }
+
+            var blueRedIndex = 0
+            var nDuplicateBlueRed = 0
+            val listBlueRed = arrayListOf<Table4ListItem>()
+            for (n in 1..120) {
+                listBlueRed.add(Table4ListItem(n, 0))
+            }
+            state.resultList?.forEachIndexed { index, result ->
+                Log.d("resultList", ""+index + "::" + result.toString())
+                if (index == 0) {
+                    Log.d("resultList", ""+index + "::" + result.year.yearNumber)
+                    listYear[index].yearNumber = result.year.yearNumber
+                    listBigSmall[index].color = getLargeSmall(result.smallLarge.name)
+                    listRange[index].rangeNumber = getRange(result.range.name)
+                    listOddEven[index].oddEvenNumber = getOddEven(result.oddEven.name)
+                    listBlueRed[index].blueRedNumber = getBlueRed(result.blueRed.name)
+                } else {
+                    if (result.year.yearNumber == state.resultList!![index - 1].year.yearNumber) {
+                        val isYear = result.year.yearNumber
+                        nDuplicateYear += 1
+                        if(nDuplicateYear==6){
+                            listYear[index - yearIndex].yearNumber = result.year.yearNumber
+                            nDuplicateYear = 0
+                        }else{
+                            yearIndex += sameYear(index, listYear, yearIndex, isYear, nDuplicateYear)
+                        }
+                    } else {
+                        listYear[index - yearIndex].yearNumber = result.year.yearNumber
+                        nDuplicateYear = 0
+                    }
+
+                    if (result.smallLarge.name == state.resultList!![index - 1].smallLarge.name) {
+                        val isBigSmall = getLargeSmall(result.smallLarge.name)
+                        nDuplicateBigSmall += 1
+                        if(nDuplicateBigSmall==6){
+                            listBigSmall[index - bigSmallIndex].color = getLargeSmall(result.smallLarge.name)
+                            nDuplicateBigSmall = 0
+                        }else{
+                            bigSmallIndex += sameBigSmall(index, listBigSmall, bigSmallIndex, isBigSmall, nDuplicateBigSmall)
+                        }
+                    } else {
+                        listBigSmall[index - bigSmallIndex].color = getLargeSmall(result.smallLarge.name)
+                        nDuplicateBigSmall = 0
+                    }
+
+                    if (result.range.name == state.resultList!![index - 1].range.name) {
+                        val range = getRange(result.range.name)
+                        nDuplicateRange += 1
+                        if(nDuplicateRange == 6){
+                            listRange[index - rangeIndex].rangeNumber = getRange(result.range.name)
+                            nDuplicateRange = 0
+                        }else{
+                            rangeIndex += sameRange(index, listRange, rangeIndex, range, nDuplicateRange)
+                        }
+                    } else {
+                        listRange[index - rangeIndex].rangeNumber = getRange(result.range.name)
+                        nDuplicateRange = 0
+                    }
+
+
+                    if (result.oddEven.name == state.resultList!![index - 1].oddEven.name) {
+                        val isOddEven = getOddEven(result.oddEven.name)
+                        nDuplicateOddEven += 1
+                        if(nDuplicateOddEven==6){
+                            nDuplicateOddEven = 0
+                            listOddEven[index - oddEvenIndex].oddEvenNumber = getOddEven(result.oddEven.name)
+                        }else{
+                            oddEvenIndex += sameOddEven(index, listOddEven, oddEvenIndex, isOddEven, nDuplicateOddEven)
+                        }
+                    } else {
+                        listOddEven[index - oddEvenIndex].oddEvenNumber = getOddEven(result.oddEven.name)
+                        nDuplicateOddEven = 0
+                    }
+
+
+                    if (result.blueRed.name == state.resultList!![index - 1].blueRed.name) {
+                        val isBlueRed = getBlueRed(result.blueRed.name)
+                        nDuplicateBlueRed += 1
+                        if(nDuplicateBlueRed==6){
+                            listBlueRed[index - blueRedIndex].blueRedNumber = getBlueRed(result.blueRed.name)
+                            nDuplicateBlueRed = 0
+                        }else{
+                            blueRedIndex += sameBlueRed(index, listBlueRed, blueRedIndex, isBlueRed, nDuplicateBlueRed)
+                        }
+                    } else {
+                        listBlueRed[index - blueRedIndex].blueRedNumber = getBlueRed(result.blueRed.name)
+                        nDuplicateBlueRed = 0
+                    }
+                }
+            }
+            adapterTable0.submitList(listYear as List<BaseListItem>?)
+            adapterTable1.submitList(listBigSmall as List<BaseListItem>?)
+            adapterTable2.submitList(listRange as List<BaseListItem>?)
+            adapterTable3.submitList(listOddEven as List<BaseListItem>?)
+            adapterTable4.submitList(listBlueRed as List<BaseListItem>?)
+        }
+    }
+
+    private fun sameYear(columnIndex: Int, arrayList: ArrayList<Table0ListItem>, yearIndex: Int, isYear: Int, nDuplicateYear: Int): Int {
+        var betweenIndex = 0
+        if (arrayList[columnIndex + 19].yearNumber == 0) {//free space
+            if (nDuplicateYear in 2..5) {
+                for (i in 2..nDuplicateYear) arrayList[(columnIndex - yearIndex) + (i * 20) - 1].yearNumber = isYear
+            } else {
+                arrayList[(columnIndex - yearIndex + 19)].yearNumber = isYear
+            }
+            betweenIndex++
+            return betweenIndex
+        }
+        return 0
+    }
+
+    private fun sameBigSmall(columnIndex: Int, arrayList: ArrayList<Table1ListItem>, bigSmallIndex: Int, isBigSmall: Int, nDuplicateBigSmall: Int): Int {
+        var betweenIndex = 0
+        if (arrayList[columnIndex + 19].color == 0) {//free space
+            if (nDuplicateBigSmall in 2..5) {
+                for (i in 2..nDuplicateBigSmall) arrayList[(columnIndex - bigSmallIndex) + (i * 20) - 1].color = isBigSmall
+            } else {
+                arrayList[(columnIndex - bigSmallIndex + 19)].color = isBigSmall
+            }
+            betweenIndex++
+            return betweenIndex
+        }
+        return 0
+    }
+
+    private fun sameRange(columnIndex: Int, arrayList: ArrayList<Table2ListItem>, rangeIndex: Int, range: Int, nDuplicateRange: Int): Int {
+        var betweenIndex = 0
+        if (arrayList[columnIndex + 19].rangeNumber == 0) {//free space
+            if (nDuplicateRange > 1) {
+                for (i in 2..nDuplicateRange) arrayList[(columnIndex - rangeIndex) + (i * 20) - 1].rangeNumber = range
+            } else {
+                arrayList[(columnIndex - rangeIndex + 19)].rangeNumber = range
+            }
+            betweenIndex++
+            return betweenIndex
+        }
+        return 0
+    }
+
+    private fun sameOddEven(columnIndex: Int, arrayList: ArrayList<Table3ListItem>, oddEvenIndex: Int, isOddEven: Int, nDuplicateOddEven: Int): Int {
+        var betweenIndex = 0
+        if (arrayList[columnIndex + 19].oddEvenNumber == 0) {//free space
+            if (nDuplicateOddEven in 2..5) {
+                for (i in 2..nDuplicateOddEven) arrayList[(columnIndex - oddEvenIndex) + (i * 20) - 1].oddEvenNumber = isOddEven
+            } else {
+                arrayList[(columnIndex - oddEvenIndex + 19)].oddEvenNumber = isOddEven
+            }
+            betweenIndex++
+            return betweenIndex
+        }
+        return 0
+    }
+
+    private fun sameBlueRed(columnIndex: Int, arrayList: ArrayList<Table4ListItem>, blueRedIndex: Int, isBlueRed: Int, nDuplicateBlueRed: Int): Int {
+        var betweenIndex = 0
+        if (arrayList[columnIndex + 19].blueRedNumber == 0) {//free space
+            if (nDuplicateBlueRed in 2..5) {
+                for (i in 2..nDuplicateBlueRed) arrayList[(columnIndex - blueRedIndex) + (i * 20) - 1].blueRedNumber = isBlueRed
+            } else {
+                arrayList[(columnIndex - blueRedIndex + 19)].blueRedNumber = isBlueRed
+            }
+            betweenIndex++
+            return betweenIndex
+        }
+        return 0
+    }
+
+    private fun getLargeSmall(type: String): Int {
+        when (type.toLowerCase()) {
+            "ស្តើង" -> {
+                return 1//blue
+            }
+            "ក្រាស់" -> {
+                return 2//red
+            }
+        }
+        return 0
+    }
+
+    private fun getRange(type: String): Int {
+        when (type) {
+            "1-2-3" -> {
+                return 1
+            }
+            "4-5-6" -> {
+                return 4
+            }
+            "7-8-9" -> {
+                return 7
+            }
+            "10-11-12" -> {
+                return 10
+            }
+        }
+        return 0
+    }
+    private fun getOddEven(type: String): Int {
+        when (type.toLowerCase()) {
+            "សេស" -> {
+                return 1//blue
+            }
+            "គូ" -> {
+                return 2//red
+            }
+        }
+        return 0
+    }
+    private fun getBlueRed(type: String): Int {
+        when (type.toLowerCase()) {
+            "ខៀវ" -> {
+                return 1//blue
+            }
+            "ក្រហម" -> {
+                return 2//red
+            }
+        }
+        return 0
+    }
+
+    @SuppressLint("SetTextI18n")
+    fun getMessage(lotto: SocketClientTimeDataModel) {
+        if (lotto.isCountDowning() && lotto.time == 128) {
+            (requireActivity() as? ShareActivity)?.apply {
+                shareViewModel.remoteResult()
+            }
+        }
+    }
+}

+ 10 - 0
app/src/main/java/com/khmer9/yuki/screen/result/ResultViewState.kt

@@ -0,0 +1,10 @@
+package com.khmer9.yuki.screen.result
+
+import com.khmer9.yuki.remote.ResultLotto
+
+data class ResultViewState(
+    val initial: Boolean = true,
+    val isProgress: Boolean = false,
+    val error: String? = null,
+    var resultList: List<ResultLotto>? = null,
+)

+ 19 - 0
app/src/main/java/com/khmer9/yuki/screen/result/Table0Adapter.kt

@@ -0,0 +1,19 @@
+package com.khmer9.yuki.screen.result
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+
+class Table0Adapter : BaseListAdapter() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
+        Table0ListViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.item_box_number,
+                parent,
+                false
+            )
+        )
+}

+ 11 - 0
app/src/main/java/com/khmer9/yuki/screen/result/Table0ListItem.kt

@@ -0,0 +1,11 @@
+package com.khmer9.yuki.screen.result
+
+import com.gdtlib.lib.adapter.BaseListItem
+
+class Table0ListItem(
+    val Id: Int,
+    var yearNumber : Int //1->5
+
+) : BaseListItem() {
+    override fun getUnique(): String = Id.toString()
+}

+ 62 - 0
app/src/main/java/com/khmer9/yuki/screen/result/Table0ListViewHolder.kt

@@ -0,0 +1,62 @@
+package com.khmer9.yuki.screen.result
+
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import com.gdtlib.lib.adapter.BaseListItem
+import com.gdtlib.lib.viewholder.BaseListViewHolder
+import com.khmer9.yuki.R
+
+class Table0ListViewHolder(
+    itemView: View
+) : BaseListViewHolder(itemView) {
+    private val ivCircle = itemView.findViewById<ImageView>(R.id.img_color)
+    private val tvNumber = itemView.findViewById<TextView>(R.id.tv_number)
+    override fun bindView(item: BaseListItem) {
+        when (item) {
+            is Table0ListItem -> {
+                with(item) {
+                    tvNumber.text = yearNumber.toString()
+                    when (yearNumber) {
+                        1 -> {//1 is blue
+                            ivCircle.setImageResource(R.drawable.bg_circle_blue)
+                        }
+                        2 -> {//2 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_blue)
+                        }
+                        3 -> {//3 is blue
+                            ivCircle.setImageResource(R.drawable.bg_circle_blue)
+                        }
+                        4 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_red)
+                        }
+                        5 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_red)
+                        }
+                        6 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_red)
+                        }
+                        7 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_blue)
+                        }
+                        8 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_blue)
+                        }
+                        9 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_blue)
+                        }
+                        10 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_red)
+                        }
+                        11 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_red)
+                        }
+                        12 -> {//4 is red
+                            ivCircle.setImageResource(R.drawable.bg_circle_red)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 19 - 0
app/src/main/java/com/khmer9/yuki/screen/result/Table1Adapter.kt

@@ -0,0 +1,19 @@
+package com.khmer9.yuki.screen.result
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gdtlib.lib.adapter.BaseListAdapter
+import com.khmer9.yuki.R
+
+class Table1Adapter : BaseListAdapter() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
+        Table1ListViewHolder(
+            LayoutInflater.from(parent.context).inflate(
+                R.layout.item_box,
+                parent,
+                false
+            )
+        )
+}

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels