Commit fc123e64 authored by UIUANG\Zsc's avatar UIUANG\Zsc

第一版1.00.000

parent bf793146
Pipeline #236 canceled with stages
*.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
local.properties
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>
\ No newline at end of file
<?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="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$USER_HOME$/.gradle/wrapper/dists/gradle-6.1.1-all/cfmwm155h49vnt3hynmlrsdst/gradle-6.1.1" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/base" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?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="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</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="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://maven.google.com" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.ypsx.yppos"
minSdkVersion 21
targetSdkVersion 30
versionCode 100000
versionName "1.00.000"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
dataBinding = true
}
//使用Kotlin实验特性
androidExtensions {
experimental = true
}
signingConfigs {
config {
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_KEY_PASSWORD
}
}
buildTypes {
debug {
buildConfigField 'String', 'HOST', DEBUG_HOST
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
// return_test {
// buildConfigField 'String', 'HOST', RETURN_DEBUG_HOST
// minifyEnabled false
//
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// }
release {
buildConfigField 'String', 'HOST', RELEASE_HOST
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
lintOptions {
disable 'InvalidPackage'
disable "ResourceType"
abortOnError false
}
dexOptions {
javaMaxHeapSize "4g"
jumboMode = true
preDexLibraries = false
additionalParameters = [
'--multi-dex',//多分包
'--set-max-idx-number=60000'//每个包内方法数上限
]
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation project(path: ':base')
implementation files('libs/platform_sdk_v3.1.0326.jar')
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.cardview:cardview:1.0.0'
implementation "com.afollestad.material-dialogs:lifecycle:3.3.0"
implementation "com.afollestad.material-dialogs:core:3.3.0"
//微信开源项目,替代SP
implementation 'com.tencent:mmkv:1.2.10'
implementation 'com.blankj:utilcodex:1.26.0'
//第三方recyclerview
implementation 'com.yanzhenjie.recyclerview:x:1.3.2'
implementation 'com.vmadalin:easypermissions-ktx:1.0.0'
//管理界面状态库
implementation 'com.kingja.loadsir:loadsir:1.3.8'
//屏幕适配
implementation 'me.jessyan:autosize:1.2.1'
implementation 'com.elvishew:xlog:1.10.1'
//BaseAdapter
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
def room_version = "2.3.0"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor "androidx.room:room-compiler:$room_version"
// To use Kotlin annotation processing tool (kapt)
kapt("androidx.room:room-compiler:$room_version")
// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$room_version")
implementation 'io.github.razerdp:BasePopup:3.1.8'
implementation 'com.github.jenly1314.AppUpdater:app-updater:1.1.0'
implementation 'com.github.getActivity:ToastUtils:9.5'
implementation 'com.aliyun.dpa:oss-android-sdk:2.1.0'
// val work_version = "2.6.0"
// Kotlin + coroutines
// implementation("androidx.work:work-runtime-ktx:$work_version")
//防崩溃
// implementation 'cat.ereza:customactivityoncrash:2.3.0'
}
\ No newline at end of file
File added
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.ypsx.yppos
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.ypsx.pos", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ypsx.yppos">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE "/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<application
android:name=".PosApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.YPPos">
<activity android:name=".ui.activity.LoginActivity"/>
<activity android:name=".ui.activity.RegisterActivity" >
</activity>
<activity android:name=".ui.activity.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.activity.MainActivity"
android:windowSoftInputMode="adjustPan"
>
</activity>
<service android:name=".jobs.LogService"/>
<service android:name=".jobs.SyncProductService"/>
<meta-data
android:name="design_width_in_dp"
android:value="360"/>
<meta-data
android:name="design_height_in_dp"
android:value="640"/>
</application>
</manifest>
\ No newline at end of file
package com.ypsx.yppos
import android.os.Build
import com.elvishew.xlog.LogConfiguration
import com.elvishew.xlog.LogLevel
import com.elvishew.xlog.XLog
import com.elvishew.xlog.formatter.border.DefaultBorderFormatter
import com.elvishew.xlog.formatter.message.json.DefaultJsonFormatter
import com.elvishew.xlog.formatter.message.throwable.DefaultThrowableFormatter
import com.elvishew.xlog.formatter.message.xml.DefaultXmlFormatter
import com.elvishew.xlog.formatter.stacktrace.DefaultStackTraceFormatter
import com.elvishew.xlog.formatter.thread.DefaultThreadFormatter
import com.elvishew.xlog.interceptor.BlacklistTagsFilterInterceptor
import com.elvishew.xlog.printer.AndroidPrinter
import com.elvishew.xlog.printer.ConsolePrinter
import com.elvishew.xlog.printer.Printer
import com.elvishew.xlog.printer.file.FilePrinter
import com.elvishew.xlog.printer.file.backup.FileSizeBackupStrategy
import com.elvishew.xlog.printer.file.backup.FileSizeBackupStrategy2
import com.elvishew.xlog.printer.file.backup.NeverBackupStrategy
import com.elvishew.xlog.printer.file.clean.FileLastModifiedCleanStrategy
import com.elvishew.xlog.printer.file.naming.DateFileNameGenerator
import com.tencent.mmkv.MMKV
import com.ypsx.base.base.BaseApp
import com.ypsx.yppos.app.event.AppViewModel
import com.ypsx.yppos.app.event.EventViewModel
import com.ypsx.yppos.room.database.PosDatabase
import com.ypsx.yppos.utils.PathUtils
import com.ypsx.yppos.utils.XLogFlattener
import com.ypsx.yppos.utils.dXLog
const val maxSize: Long = 1024 * 1024 * 50
const val maxTimeMillis: Long = 1000 * 60 * 60 * 24 * 30
const val maxBackupIndex: Int = 10
class PosApp : BaseApp() {
companion object {
lateinit var instance: PosApp
lateinit var eventViewModelInstance: EventViewModel
lateinit var appViewModelInstance: AppViewModel
}
override fun onCreate() {
super.onCreate()
MMKV.initialize(this)
instance = this
PosDatabase.getDatabase(instance)
eventViewModelInstance = getAppViewModelProvider().get(EventViewModel::class.java)
appViewModelInstance = getAppViewModelProvider().get(AppViewModel::class.java)
initXlog()
}
private fun initXlog() {
val config = LogConfiguration.Builder()
.logLevel(LogLevel.ALL)// 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL
.tag(getString(R.string.global_tag)) // 指定 TAG,默认为 "X-LOG"
.enableThreadInfo() // 允许打印线程信息,默认禁止
.enableStackTrace(2) // 允许打印深度为 2 的调用栈信息,默认禁止
.enableBorder() // 允许打印日志边框,默认禁止
.jsonFormatter(DefaultJsonFormatter()) // 指定 JSON 格式化器,默认为 DefaultJsonFormatter
.xmlFormatter(DefaultXmlFormatter()) // 指定 XML 格式化器,默认为 DefaultXmlFormatter
.throwableFormatter(DefaultThrowableFormatter()) // 指定可抛出异常格式化器,默认为 DefaultThrowableFormatter
.threadFormatter(DefaultThreadFormatter()) // 指定线程信息格式化器,默认为 DefaultThreadFormatter
.stackTraceFormatter(DefaultStackTraceFormatter()) // 指定调用栈信息格式化器,默认为 DefaultStackTraceFormatter
.borderFormatter(DefaultBorderFormatter()) // 指定边框格式化器,默认为 DefaultBorderFormatter
.build()
val androidPrinter: Printer = AndroidPrinter(true) // 通过 android.util.Log 打印日志的打印器
// val consolePrinter: Printer = ConsolePrinter() // 通过 System.out 打印日志到控制台的打印器
val filePrinter: Printer = FilePrinter.Builder(PathUtils.getLogParent()) // 指定保存日志文件的路径
.fileNameGenerator(DateFileNameGenerator()) // 指定日志文件名生成器,默认为 ChangelessFileNameGenerator("log")
.backupStrategy(NeverBackupStrategy()) // 指定日志文件备份策略,默认为 FileSizeBackupStrategy(1024 * 1024)
.backupStrategy(
FileSizeBackupStrategy2(maxSize, maxBackupIndex)
) // Default: FileSizeBackupStrategy(1024 * 1024)
.cleanStrategy(FileLastModifiedCleanStrategy(maxTimeMillis)) // Default: NeverCleanStrategy(), 7天
.flattener(XLogFlattener()) // 指定日志平铺器,默认为 DefaultFlattener
.build()
XLog.init( // 初始化 XLog
config, // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build()
androidPrinter, // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter(Android)/ConsolePrinter(java)
filePrinter
)
val header = "\n>>>>>>>>>>>>>>>> File Header >>>>>>>>>>>>>>>>" +
"\nDevice Manufacturer: " + Build.MANUFACTURER +
"\nDevice Brand : " + Build.BRAND +
"\nDevice Model : " + Build.MODEL +
"\nAndroid Version : " + Build.VERSION.RELEASE +
"\nAndroid SDK : " + Build.VERSION.SDK_INT +
"\nApp VersionName : " + BuildConfig.VERSION_NAME +
"\nApp VersionCode : " + BuildConfig.VERSION_CODE +
"\n<<<<<<<<<<<<<<<< File Header <<<<<<<<<<<<<<<<\n\n";
header.dXLog()
}
}
\ No newline at end of file
package com.ypsx.yppos.app.base
import android.os.Bundle
import androidx.databinding.ViewDataBinding
import com.ypsx.base.base.activity.BaseVmDbActivity
import com.ypsx.base.base.viewmodel.BaseViewModel
import com.ypsx.yppos.app.ext.dismissLoadingExt
import com.ypsx.yppos.app.ext.showLoadingExt
/**
* 时间 : 2019/12/21
* 作者 : hegaojian
* 描述 : 你项目中的Activity基类,在这里实现显示弹窗,吐司,还有加入自己的需求操作 ,如果不想用Databind,请继承
* BaseVmActivity例如
* abstract class BaseActivity<VM : BaseViewModel> : BaseVmActivity<VM>() {
*/
abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : BaseVmDbActivity<VM, DB>() {
abstract override fun layoutId(): Int
abstract override fun initView(savedInstanceState: Bundle?)
/**
* 创建liveData观察者
*/
override fun createObserver() {}
/**
* 打开等待框
*/
override fun showLoading(message: String) {
showLoadingExt(message)
}
/**
* 关闭等待框
*/
override fun dismissLoading() {
dismissLoadingExt()
}
/* *//**
* 在任何情况下本来适配正常的布局突然出现适配失效,适配异常等问题,只要重写 Activity 的 getResources() 方法
*//*
override fun getResources(): Resources {
AutoSizeCompat.autoConvertDensityOfGlobal(super.getResources())
return super.getResources()
}*/
}
\ No newline at end of file
package com.ypsx.yppos.app.base
import android.os.Bundle
import androidx.databinding.ViewDataBinding
import com.ypsx.base.base.fragment.BaseVmDbFragment
import com.ypsx.base.base.viewmodel.BaseViewModel
import com.ypsx.yppos.app.ext.dismissLoadingExt
import com.ypsx.yppos.app.ext.hideSoftKeyboard
import com.ypsx.yppos.app.ext.showLoadingExt
/**
* 作者 : hegaojian
* 时间 : 2019/12/21
* 描述 : 你项目中的Fragment基类,在这里实现显示弹窗,吐司,还有自己的需求操作 ,如果不想用Databind,请继承
* BaseVmFragment例如
* abstract class BaseFragment<VM : BaseViewModel> : BaseVmFragment<VM>() {
*/
abstract class BaseFragment<VM : BaseViewModel, DB : ViewDataBinding> : BaseVmDbFragment<VM, DB>() {
/**
* 当前Fragment绑定的视图布局
*/
abstract override fun layoutId(): Int
abstract override fun initView(savedInstanceState: Bundle?)
/**
* 懒加载 只有当前fragment视图显示时才会触发该方法
*/
override fun lazyLoadData() {}
/**
* 创建LiveData观察者 Fragment执行onViewCreated后触发
*/
override fun createObserver() {}
/**
* Fragment执行onViewCreated后触发
*/
override fun initData() {
}
/**
* 打开等待框
*/
override fun showLoading(message: String) {
showLoadingExt(message)
}
/**
* 关闭等待框
*/
override fun dismissLoading() {
dismissLoadingExt()
}
override fun onPause() {
super.onPause()
hideSoftKeyboard(activity)
}
/**
* 延迟加载 防止 切换动画还没执行完毕时数据就已经加载好了,这时页面会有渲染卡顿 bug
* 这里传入你想要延迟的时间,延迟时间可以设置比转场动画时间长一点 单位: 毫秒
* 不传默认 300毫秒
* @return Long
*/
override fun lazyLoadTime(): Long {
return 300
}
}
\ No newline at end of file
package com.ypsx.yppos.app.event
import com.ypsx.base.base.viewmodel.BaseViewModel
class AppViewModel : BaseViewModel() {
}
\ No newline at end of file
package com.ypsx.yppos.app.event
import com.ypsx.base.base.viewmodel.BaseViewModel
class EventViewModel : BaseViewModel() {
}
\ No newline at end of file
package com.ypsx.yppos.app.ext
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.fragment.app.Fragment
import com.ypsx.base.ext.util.inputMethodManager
inline fun <reified T : Activity> Activity.startKtxActivity(
vararg values: Pair<String, Any>,
flag: Int? = null,
extra: Bundle? = null
) =
startActivity(getIntent<T>(flag, extra, *values))
inline fun <reified T : Activity> Activity.startKtxActivityFinish(
){
startActivity(Intent(this, T::class.java))
finish()
}
inline fun <reified T : Activity> Fragment.startKtxActivity(
flag: Int? = null,
extra: Bundle? = null,
vararg values: Pair<String, Any>
) =
activity?.let {
startActivity(it.getIntent<T>(flag, extra, *values))
}
inline fun <reified T : Activity> Context.startKtxActivity(
flag: Int? = null,
extra: Bundle? = null,
vararg values: Pair<String, Any>
) =
startActivity(getIntent<T>(flag, extra, *values))
inline fun <reified T : Activity> Activity.startKtxActivityForResult(
requestCode: Int,
flag: Int? = null,
extra: Bundle? = null,
vararg values: Pair<String, Any>
) =
startActivityForResult(getIntent<T>(flag, extra, *values), requestCode)
inline fun <reified T : Activity> Fragment.startKtxActivityForResult(
requestCode: Int,
flag: Int? = null,
extra: Bundle? = null,
vararg values: Pair<String, Any>
) =
activity?.let {
startActivityForResult(activity?.getIntent<T>(flag, extra, *values), requestCode)
}
inline fun <reified T : Context> Context.getIntent(
flag: Int? = null,
extra: Bundle? = null,
vararg pairs: Pair<String, Any>
): Intent =
Intent(this, T::class.java).apply {
flag?.let { setFlags(flags) }
extra?.let { putExtras(extra) }
pairs.forEach { pair ->
val name = pair.first
when (val value = pair.second) {
is Int -> putExtra(name, value)
is Byte -> putExtra(name, value)
is Char -> putExtra(name, value)
is Short -> putExtra(name, value)
is Boolean -> putExtra(name, value)
is Long -> putExtra(name, value)
is Float -> putExtra(name, value)
is Double -> putExtra(name, value)
is String -> putExtra(name, value)
is CharSequence -> putExtra(name, value)
is Parcelable -> putExtra(name, value)
is Array<*> -> putExtra(name, value)
is ArrayList<*> -> putExtra(name, value)
// is Serializable -> putExtra(name, value)
is BooleanArray -> putExtra(name, value)
is ByteArray -> putExtra(name, value)
is ShortArray -> putExtra(name, value)
is CharArray -> putExtra(name, value)
is IntArray -> putExtra(name, value)
is LongArray -> putExtra(name, value)
is FloatArray -> putExtra(name, value)
is DoubleArray -> putExtra(name, value)
is Bundle -> putExtra(name, value)
is Intent -> putExtra(name, value)
else -> {
}
}
}
}
fun Activity.hideKeyboard() {
inputMethodManager?.hideSoftInputFromWindow((currentFocus ?: View(this)).windowToken, 0)
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
currentFocus?.clearFocus()
}
fun Activity.showKeyboard(et: EditText) {
et.requestFocus()
inputMethodManager?.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT)
}
fun Activity.hideKeyboard(view: View) {
inputMethodManager?.hideSoftInputFromWindow(view.windowToken, 0)
}
\ No newline at end of file
package com.ypsx.yppos.app.ext
import android.app.Activity
import android.view.inputmethod.InputMethodManager
/**
* 隐藏软键盘
*/
fun hideSoftKeyboard(activity: Activity?) {
activity?.let { act ->
val view = act.currentFocus
view?.let {
val inputMethodManager =
act.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(
view.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}
}
}
\ No newline at end of file
package com.ypsx.yppos.app.ext
import android.app.Activity
fun intentFinish(activity: Activity) {
}
\ No newline at end of file
package com.ypsx.yppos.app.ext
import android.app.Activity
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView
import com.afollestad.materialdialogs.lifecycle.lifecycleOwner
import com.ypsx.yppos.R
/**
* @author : hgj
* @date : 2020/6/28
*/
private var loadingDialog: MaterialDialog? = null
/**
* 打开等待框
*/
fun AppCompatActivity.showLoadingExt(message: String = "请求网络中") {
if (!this.isFinishing) {
if (loadingDialog == null) {
loadingDialog = MaterialDialog(this)
.cancelable(true)
.cancelOnTouchOutside(false)
.cornerRadius(12f)
.customView(R.layout.layout_custom_progress_dialog_view)
.lifecycleOwner(this)
loadingDialog?.getCustomView()?.run {
this.findViewById<TextView>(R.id.loading_tips).text = message
}
}
loadingDialog?.show()
}
}
/**
* 打开等待框
*/
fun Fragment.showLoadingExt(message: String = "请求网络中") {
activity?.let {
if (!it.isFinishing) {
if (loadingDialog == null) {
loadingDialog = MaterialDialog(it)
.cancelable(true)
.cancelOnTouchOutside(false)
.cornerRadius(12f)
.customView(R.layout.layout_custom_progress_dialog_view)
.lifecycleOwner(this)
loadingDialog?.getCustomView()?.run {
this.findViewById<TextView>(R.id.loading_tips).text = message
}
}
loadingDialog?.show()
}
}
}
/**
* 关闭等待框
*/
fun Activity.dismissLoadingExt() {
loadingDialog?.dismiss()
loadingDialog = null
}
/**
* 关闭等待框
*/
fun Fragment.dismissLoadingExt() {
loadingDialog?.dismiss()
loadingDialog = null
}
package com.ypsx.yppos.http
import android.os.Build
import com.ypsx.base.util.AppUtils
import com.ypsx.yppos.http.data.bean.*
import com.ypsx.yppos.http.data.request.*
import com.ypsx.yppos.room.entity.PosProduct
import com.ypsx.yppos.utils.CacheUtil
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query
interface ApiService {
/**
* 申请pos机
*/
@POST("newretail/api/pos/machine/apply")
suspend fun applyPos(
@Body applyPos: ApplyPosMachineRequest,
): ApiResponse<String>
/**
* 根据POS机Id查询POS机详情
*/
@GET("newretail/api/pos/machine/getById")
suspend fun getById(@Query("id") id: String): ApiResponse<PosMachineResponse>
/**
* pos机用户登录
*/
@POST("newretail/api/sys/user/pos/login")
suspend fun posLogin(@Body loginRequest: PosLoginRequest): ApiResponse<LoginResponse>
/**
* 注销登录
*/
@POST("newretail/api/sys/user/pos/logout")
suspend fun posLogout(): ApiResponse<String?>
@POST("newretail/api/sys/user/kickoff")
suspend fun posKickOff(@Body request: KickoffRequest): ApiResponse<String?>
/**
* 根据POS机Id查询POS机配置详情
*/
@GET("newretail/api/pos/config/getByPosId")
suspend fun getByPosId(@Query("posId") posId: String): ApiResponse<PosConfigResponse>
/**
* 增量下载POS门店商品
*/
@GET("newretail/api/mall/product/store/incrementDownload")
suspend fun getProductList(
@Query("storeId") storeId: String,
@Query("updateTimeBigger") updateTimeBigger: String?=""
): ApiResponse<List<PosProduct>>
/**
* 取得App最新版本信息
*/
@GET("newretail/api/sys/app/version/getLastest")
suspend fun getLastAppVersion(
@Query("posId") posId: String,
@Query("type") type: String = "ypsxAndroidPos",
@Query("osVersion") osVersion: String = Build.VERSION.RELEASE,
@Query("version") version: Long = AppUtils.getVersionCode()
): ApiResponse<AppVersionResponse?>
/********************************************处理日志*************************************************/
/**
* 根据POS机编码查询POS机命令
*/
@GET("newretail/api/pos/machine/command/getByPosCode")
suspend fun getCommandByPosCode(@Query("posCode") posCode: String)
: ApiResponse<List<SimplePosCommandResponse>>
/**
* 回复POS机命令,标记已收到命令,准备执行
*/
@POST("newretail/api/pos/machine/command/reply")
suspend fun replyCommand(@Body commandIds: ReplyCommandListRequest): ApiResponse<String>
/**
* 保存命令处理结果
*
*/
@POST("newretail/api/pos/machine/command/saveResult")
suspend fun saveCommandResult(@Body saveCommandResult: SaveCommandResultRequest): ApiResponse<String>
// /**
// * 根据小票号查询订单
// */
// @GET("/api/mall/order/getByOrderNo")
// suspend fun
@POST("newretail/api/mall/promotion/getProductPromotions")
suspend fun getProductPromotions(@Body request: ProductPromotionRequest): ApiResponse<ProductPromotionsResponse>
/**
* 上传订单 返回订单详情
*/
@POST("newretail/api/mall/order/upload")
suspend fun uploadOrder(@Body request: List<InsertOrderRequest>): ApiResponse<List<UploadOrderResponse>>
/**
* 获得对应的支付订单签名,对于不需要支付签名的支付方式,会直接完成支付
*/
@POST("newretail/api/payment/unifiedpay/getCashPaySign")
suspend fun getCashPaySign(@Body request: UnifiedCashPaySignRequest): ApiResponse<String?>
/**
* 根据订单标识查询具体订单状态
*/
@GET("newretail/api/mall/order/getOrderStatusById")
suspend fun getOrderStatusById(@Query("orderId") orderId: String): ApiResponse<OrderStatusResponse>
@GET("newretail/api/mall/order/getDetailsById")
suspend fun getOrderDetailById(@Query("orderId") orderId: String): ApiResponse<OrderDetailsResponse>
@GET("newretail/api/sys/options/query")
suspend fun getOptionsByKeys(@Query("keys") keys: String = CacheUtil.getAliYunKey()): ApiResponse<List<OptionsResponse>>
}
\ No newline at end of file
package com.ypsx.yppos.http
import com.ypsx.base.util.DeviceUtils
import com.ypsx.yppos.utils.CacheUtil
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
/**
* 自定义头部参数拦截器,传入heads
*/
class HeadInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
builder.addHeader("Content-Type", "application/json;charset=utf-8").build()
builder .addHeader("Accept", "application/json;charset=utf-8").build()
val loginResponse = CacheUtil.getLoginResponse()
if (loginResponse != null && loginResponse.accessToken.isNotEmpty()) {
builder.addHeader("access-token", loginResponse.accessToken).build()
}
builder.addHeader("device", "Android").build()
builder.addHeader("mac-addr", DeviceUtils.getMacAddress()).build()
return chain.proceed(builder.build())
}
}
\ No newline at end of file
package com.ypsx.yppos.http
import com.elvishew.xlog.XLog
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.utils.eXLog
import okhttp3.*
import okhttp3.internal.http.HttpHeaders
import okio.Buffer
import java.io.EOFException
import java.io.IOException
import java.nio.charset.Charset
import java.text.MessageFormat
import java.util.concurrent.TimeUnit
class HttpLoggingInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
try {
logRequest(request)
} catch (e: Exception) {
XLog.e("", e)
}
// log resposne
val startNs = System.nanoTime()
val response = chain.proceed(request)
try {
logResponse(request, response, startNs)
} catch (e: Exception) {
e.eXLog()
}
return response
}
@Throws(Exception::class)
private fun logResponse(request: Request, response: Response, startNs: Long) {
val logBody = true
val sb = StringBuffer()
val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
val responseBody = response.body()
val contentLength = responseBody!!.contentLength()
sb.append(
"<-- " + response.code() + ' ' + response.request().url() + " (" + tookMs + "ms" + ')'
).append("\n")
val headers = response.headers()
// sb.append(
// MessageFormat.format(
// "{0}={1}\n",
// HeadInterceptor.HEADER_REQUEST_ID,
// request.header(HeadInterceptor.HEADER_REQUEST_ID)
// )
// )
var i = 0
val count = headers.size()
while (i < count) {
sb.append(headers.name(i) + ": " + headers.value(i)).append("\n")
i++
}
if (!logBody || !HttpHeaders.hasBody(response)) {
sb.append("<-- END HTTP").append("\n")
} else if (bodyEncoded(response.headers())) {
sb.append("<-- END HTTP (encoded body omitted)").append("\n")
} else {
val source = responseBody.source()
source.request(Long.MAX_VALUE) // Buffer the entire body.
val buffer = source.buffer()
var charset = UTF8
val contentType = responseBody.contentType()
if (contentType != null) {
charset = contentType.charset(UTF8)
}
if (!isPlaintext(contentType, buffer)) {
XLog.d(sb.toString())
} else if (contentLength != 0L) {
sb.append(buffer.clone().readString(charset))
}
}
sb.toString().dXLog()
}
@Throws(Exception::class)
private fun logRequest(request: Request) {
val logBody = true
val requestBody = request.body()
val hasRequestBody = requestBody != null
val sb = StringBuffer()
sb.append("--> " + request.method() + ' ' + request.url()).append("\n")
val headers = request.headers()
var i = 0
val count = headers.size()
while (i < count) {
val name = headers.name(i)
sb.append(name + ": " + headers.value(i)).append("\n")
i++
}
if (!logBody || !hasRequestBody) {
sb.append("--> END " + request.method()).append("\n")
} else if (bodyEncoded(request.headers())) {
sb.append("--> END " + request.method() + " (encoded body omitted)").append("\n")
} else {
val buffer = Buffer()
requestBody!!.writeTo(buffer)
var charset = UTF8
val contentType = requestBody.contentType()
if (contentType != null) {
charset = contentType.charset(UTF8)
}
if (isPlaintext(contentType, buffer)) {
sb.append(buffer.readString(charset)).append("\n")
} else {
sb.append(
"--> END " + request.method() + " (binary "
+ requestBody.contentLength() + "-byte body omitted)"
).append("\n")
}
}
sb.toString().dXLog()
}
private fun bodyEncoded(headers: Headers): Boolean {
val contentEncoding = headers["Content-Encoding"]
return contentEncoding != null && !contentEncoding.equals("identity", ignoreCase = true)
}
companion object {
private val UTF8 = Charset.forName("UTF-8")
/**
* Returns true if the body in question probably contains human readable text. Uses a small sample
* of code points to detect unicode control characters commonly used in binary file signatures.
*/
private fun isPlaintext(contentType: MediaType?, buffer: Buffer): Boolean {
if (contentType != null) {
val contentTypeString = contentType.toString().toLowerCase()
if (contentTypeString.contains("image") || contentTypeString.contains("multipart")) {
return false
} else if (contentTypeString.contains("json") || contentTypeString.contains("text") || contentTypeString.contains(
"html"
)
) {
return true
}
}
return try {
val prefix = Buffer()
val byteCount = if (buffer.size() < 64) buffer.size() else 64
buffer.copyTo(prefix, 0, byteCount)
for (i in 0..15) {
if (prefix.exhausted()) {
break
}
val codePoint = prefix.readUtf8CodePoint()
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false
}
}
true
} catch (e: EOFException) {
false // Truncated UTF-8 sequence.
}
}
}
}
\ No newline at end of file
package com.ypsx.yppos.http
import com.google.gson.GsonBuilder
import com.ypsx.base.base.appContext
import com.ypsx.base.network.BaseNetworkApi
import com.ypsx.base.network.interceptor.CacheInterceptor
import com.ypsx.base.network.interceptor.logging.LogInterceptor
import com.ypsx.yppos.BuildConfig
import okhttp3.Cache
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.util.concurrent.TimeUnit
/**
* 作者 : hegaojian
* 时间 : 2019/12/23
* 描述 : 网络请求构建器,继承BasenetworkApi 并实现setHttpClientBuilder/setRetrofitBuilder方法,
* 在这里可以添加拦截器,设置构造器可以对Builder做任意操作
*/
//双重校验锁式-单例 封装NetApiService 方便直接快速调用简单的接口
val apiService: ApiService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
NetworkApi.INSTANCE.getApi(ApiService::class.java, BuildConfig.HOST)
}
class NetworkApi : BaseNetworkApi() {
companion object {
val INSTANCE: NetworkApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
NetworkApi()
}
}
/**
* 实现重写父类的setHttpClientBuilder方法,
* 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意操作
*/
override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
builder.apply {
//设置缓存配置 缓存最大10M
cache(Cache(File(appContext.cacheDir, "cxk_cache"), 10 * 1024 * 1024))
//示例:添加公共heads 注意要设置在日志拦截器之前,不然Log中会不显示head信息
addInterceptor(HeadInterceptor())
//添加缓存拦截器 可传入缓存天数,不传默认7天
addInterceptor(CacheInterceptor())
// 日志拦截器
addInterceptor(HttpLoggingInterceptor())
//超时时间 连接、读、写
connectTimeout(60, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(60, TimeUnit.SECONDS)
}
return builder
}
/**
* 实现重写父类的setRetrofitBuilder方法,
* 在这里可以对Retrofit.Builder做任意操作,比如添加GSON解析器,protobuf等
*/
override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
return builder.apply {
addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
}
}
}
package com.ypsx.yppos.http.data.bean
data class ActivityInfoDTO(
//活动ID
var activityId: String,
// 活动价,单位元
var activityPrice: Double,
//活动类型,TIMESHARE_ACTIVITY是分时段活动,其他都认为是普通促销活动
var activityType: String,
//财务信息,透传给上传订单接口即可
var financeInfo: String,
//供应商分摊比例(%)
var supplierRatio: String
)
package com.ypsx.yppos.http.data.bean
import java.io.Serializable
/**
* 分页数据的基类
*/
data class ApiPagerResponse<T>(
var page: Int,//当前页数
var pageSize: Int,//每页记录数,必须大于0且小于等于100
var pageCount: Int,//总页数
var records: T,
var recordCount: Int//总记录数
) : Serializable {
/**
* 数据是否为空
*/
fun isEmpty() = (records as List<*>).size == 0
/**
* 是否为刷新
*/
fun isRefresh() = page == 1
/**
* 是否还有更多数据
*/
fun hasMore() = page * pageSize < recordCount
}
\ No newline at end of file
package com.ypsx.yppos.http.data.bean
import com.ypsx.base.network.BaseResponse
/**
* 作者 : hegaojian
* 时间 : 2019/12/23
* 描述 :服务器返回数据的基类
* 如果你的项目中有基类,那美滋滋,可以继承BaseResponse,请求时框架可以帮你自动脱壳,自动判断是否请求成功,怎么做:
* 1.继承 BaseResponse
* 2.重写isSucces 方法,编写你的业务需求,根据自己的条件判断数据是否请求成功
* 3.重写 getResponseCode、getResponseData、getResponseMsg方法,传入你的 code data msg
*/
data class ApiResponse<T>(
val code: Int,
val message: String,
var data: T,
val id: String,
) : BaseResponse<T>() {
// 这里是示例,wanandroid 网站返回的 错误码为 0 就代表请求成功,请你根据自己的业务需求来改变
override fun isSuccess() = code == 0
override fun getResponseCode() = code
override fun getResponseData() = data
override fun getResponseMsg() = message
}
\ No newline at end of file
package com.ypsx.yppos.http.data.bean
data class AppVersionResponse(
val desciption: String? = null,
val downloadUrl: String,
val id: String,
val type: String ,
val mode:String,
val versionCode: Int,
val versionName: String,
)
\ No newline at end of file
package com.ypsx.yppos.http.data.bean
data class InsertOrderProductDTO(
var activityPrice: Double = 0.0,
var activitys: ActivityInfoDTO?=null,
//成交价
var amount: Double = 0.0,
var barCode: String,
//礼品卡号
var cardNumber: String? = null,
var conversionRatio: String? = null,
//是否是删除项
var deleted: Boolean = false,
//商品项优惠总金额
var discountTotal: Double = 0.0,
//是否虚拟商品
var isVirtual: Boolean = false,
//会员价
var memberPrice: Double = 0.0,
//商品名称
var name: String,
var payApportionAmount: Double = 0.0,
var pluLevel: String? = null,
var price: Double,
var quantity:Double,
var priceType: String,
var productId: String,
var productNumber: String,
var settlementPrice: Double = 0.0,
var spec: String,
var style: String? = null,
//尾差 成交价/数量 保留两位小数后的余数
var tailAmount: Int = 0,
var unit: String,
//商家商品Id
var merchantItemId: String?=null,
//称重码
var weightCode: String? = null,
)
package com.ypsx.yppos.http.data.bean
data class LoginResponse(
var accessToken:String,
var currentTime:String,
var expiresIn:Int,
var refreshExpiresIn:Int,
var refreshToken:String,
var firstLogin:Boolean,
var loginUser:LoginUser
)
package com.ypsx.yppos.http.data.bean
data class LoginUser(
//头像URL
var avatar: String,
val birthday: String? = null,
val createTime: String,
val creatorId: String,
val creatorName: String,
val disabled: Boolean,
val email: String? = null,
val id: String,
val idNo: String? = null,
val lastLoginIp: String? = null,
val lastLoginTime: String? = null,
val mobile: String,
val orgId: String,
val orgName: String,
val permissions: List<String>,
val realName: String? = null,
val updateTime: String? = null,
val updatorId: String? = null,
val updatorName: String? = null,
//账户
val username: String? = null,
val workNumber: String? = null,
//密码
var password: String? = null,
var roles: List<String>? = null,
var workingOrg: OrganizationDTO
)
package com.ypsx.yppos.http.data.bean
data class OptionsResponse(
var id: String,
val key: String,
val value: String,
val groupName: String
)
package com.ypsx.yppos.http.data.bean
data class OrderActivityResponse(
var activityId: String,
//UNKNOWN_ACTIVITY_TYPE, TIMESHARE_ACTIVITY, OTHER_ACTIVITY, UNRECOGNIZED
var activityType: String,
var discountMoney: Double,
var productIds: List<ProductPromotionDetailDTO>
)
package com.ypsx.yppos.http.data.bean
import com.ypsx.yppos.room.entity.PosBuyPart
import com.ypsx.yppos.room.entity.PosBuyPayment
data class OrderDetailsResponse(
var id:String,
var orderNo:String,
var promotionOnSaleTotal:Double,
var discountTotal:Double,
var totalAmount:Double,
var payAmount:Double,
var changeAmount:Double,
var roundPrice:Double,
var posId:String,
var einvoiceQrcodeUrl:String = "",
var createTime:String,
var creatorId:String,
var creatorName:String,
var updateTime:String,
var buyNumber:String,
var storeId:String,
var type:String,
var platformCommissionAmount:Double,
var cashTotal:Double,
var orderAmount:Double,
var status:String,
var mobile: String? = null,
var memberId: String? = null,
var payments:List<PosBuyPayment>,
var activitys:List<OrderActivityResponse>?= null,
var deleteProducts:List<PosBuyPart>,
var products:List<OrderProductDTO>,
)
package com.ypsx.yppos.http.data.bean
data class OrderPaymentDTO(
var id: String,
var orderId: String,
var payMethod: String,
var realPayMethod: String,
var realPayFlowNo: String,
var payTime: String,
var payFlowNo: String,
var status: String,
var openId: String,
var total: Double
)
package com.ypsx.yppos.http.data.bean
data class OrderProductDTO(
var id:String,
//是否是删除项
var deleted: Boolean = false,
//商品名称
var name: String,
//成交价
var amount: Double = 0.0,
var barCode: String,
var pluLevel: String? = null,
var price: Double,
var quantity:Double,
var priceType: String,
var productId: String,
var productNumber: String,
var settlementPrice: Double = 0.0,
var tailAmount: Int = 0,
var spec: String,
var style: String? = null,
var unit: String,
//商家商品Id
var merchantItemId: String?=null,
//称重码
var weightCode: String? = null,
var discountTotal: Double = 0.00,
//是否虚拟商品
var isVirtual: Boolean = false,
var conversionRatio: String? = null,
var activityPrice: Double = 0.0,
var payApportionAmount: Double = 0.0,
var productAmount: Double = 0.0,
var memberPrice: Double = 0.0,
var paymentProductId:String,
var paymentProductQuantity:Double,
var paymentProductCode:String,
var paymentProductName:String,
var activitys:List<OrderActivityResponse>?= null,
)
package com.ypsx.yppos.http.data.bean
data class OrderStatusResponse(
//"id":"1435535288456716289","storeId":"2000000504","storeName":"去ERP测试门店1","status":"FINISHED","realPayMethod":"支付宝","realPayFlowNo":"2021090822001467681428252239"
val id:String,
val storeId:String,
val storeName:String,
val status:String,
val realPayMethod:String,
val realPayFlowNo:String,
val errorMessage:String
)
package com.ypsx.yppos.http.data.bean
data class OrganizationDTO(
val id: String,
val createTime: String,
val orgId: String,
val updateTime: String,
val updatorId: String,
val updatorName: String,
var address: String,
val code: String,
val name: String,
val path: String,
val remark: String,
val contact: String,
val creatorId: String,
val creatorName: String,
val disabled: Boolean,
val latitude: String,
val longitude: String,
val tel: String
)
package com.ypsx.yppos.http.data.bean
data class PayAmountBean(
var discountTotal: Double = 0.0,
var payApportionAmount: Double = 0.0,
var discountMoney: Double = 0.0, //满减促销
var discountTime: Double = 0.0,//分时促销
var discountSpecial: Double = 0.0,//特价促销
var amount: Double = 0.0 //总价值
)
package com.ypsx.yppos.http.data.bean
data class PosConfigResponse(
//小票底部文字,最多144个汉字
var ticketBottom: String? = null,
//是否打印电子发票
var printTicketQrcode: Boolean,
//是否打印小票条码
var printTicketBar: Boolean,
var posId: String,
//付款方式,多个值以英文逗号分隔
var payMethods: String? = null,
//删除挂单 0:全部允许 1:需店长授权
var deleteList: Int,
//整单取消授权方式: 0: 全部允许, 1:需店长授权
var cancelWholeOrder: Int,
//现金圆整方式: 0: 四舍五入
var cashRoundMode: Int,
//锁屏时间,单位分钟, =0表示不锁屏,大于0表示锁屏时间(限制5-60)
var lockScreen: Int,
//最大挂单数: 限制0-5
var maxPendingOrder: Int,
var options: HashMap<String, String>
)
package com.ypsx.yppos.http.data.bean
/**
* pos机详情
*/
data class PosMachineResponse(
val applyFailedMessage: String? = null,
val applyId: String,
val posId:String?=null,
val brand: String,
val cityName: String? = null,
val code: String? =null,
val createTime: String,
val id: String,
val macAddress: String,
val installDiskCapacity: String,
val installationDirectory: String,
val ip: String,
val lastConnectTime: String,
val networkingStatus: Boolean,
val port: Int,
val specification: String,
// APPLY, ACTIVE, APPLY_FAILED, DISABLED
val status: String,
val orgId: String,
val remark: String? = null,
val storeId: String?=null,
val storeName: String?=null,
val updateTime: String,
val updatorId: String,
val updatorName: String,
val version: String,
)
package com.ypsx.yppos.http.data.bean
data class ProductItemPromotionResponse(
//商品ID
var productId: String,
//价格成交类型,MEMBER_TYPE为会员价,SALE_TYPE为售价,UNRECOGNIZED_TYPE为未知类型
var priceType: String,
//原售价
var price: Double,
//商品数量
var quantity: Double,
//商品级总优惠金额
var discountTotal: Double,
//会员价
var memberPrice: Double,
//商家商品Id
var merchantItemId: String?=null,
//商品行平摊实付金额, 退货时退货金额不能大于该值
var payApportionAmount: Double,
var activityInfo: ActivityInfoDTO? = null
)
package com.ypsx.yppos.http.data.bean
data class ProductPromotionDetailDTO(
var financeInfo:String,
var productId:String
)
package com.ypsx.yppos.http.data.bean
data class ProductPromotionsResponse(
var orderNo: String,
var shopId: String,
var orderPromotions: List<OrderActivityResponse>?=null,
var productPromotions: List<ProductItemPromotionResponse>
)
package com.ypsx.yppos.http.data.bean
data class SimplePosCommandResponse(
//命令ID
var id: Long,
//命令参数
val parameters: String,
//命令状态
val status: String,
//命令类型
val type: String
)
package com.ypsx.yppos.http.data.bean
data class UploadOrderResponse(
// 错误消息
var errorMessage: String,
// 订单Id
var orderId: String,
//订单编号
var orderNo: String,
//是否上传成功
var success: Boolean,
var total:Double,
var authCode:String,
)
package com.ypsx.yppos.http.data.entity
enum class OrderStatusEnum {
//待付款
CREATED,
//已付款
PAID,
//付款失败
PAYFAILED,
//配送中
SHIPPED,
//待自提
PENDING,
//待评价
RECEIVED,
//已完成
FINISHED,
//作废
CANCELED,
//已退款
REJECTED,
//退款中
RETURNING,
//待发货
WAITSHIPPED,
//待拼团
WAITGROUP,
//待付尾款
WAITPAYBALANCE
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
enum class PosCommandStatus{
CREATE, CANCELED, PROCESSING, FAILED, SUCCESS
}
package com.ypsx.yppos.http.data.entity
enum class PosCommandType {
UPLOAD_LOG,UPLOAD_DB
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
enum class PosPayMethod {
CARDPAY,
//扫码聚合支付
CCB_BAR_PAY,
CASHPAY,
UNIONPAY_POS,
//扫码
OFFLINE_QRCODE_PAY,
GROUP_PAY,
BUY_CARDPAY,
CASH_TICKET_PAY,
AGGREGATE_SCAN_CODE_PAY
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
enum class PosProductStyle {
SINGLE,
SPEC_PARENT,
SPEC_CHILD,
MEALS,
MIXTURE,
WEIGHT
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
/**
* string
POS门店商品状态
*/
enum class PosShelvesStatus {
ON, OFF, DELETED
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
enum class PosStatus{
/**
* 申请中
*/
APPLY,
/**
* 可用
*/
ACTIVE,
/**
* 申请失败
*/
APPLY_FAILED,
/**
* 禁用
*/
DISABLED;
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
enum class ProductActivityType {
UNKNOWN_ACTIVITY_TYPE, TIMESHARE_ACTIVITY, OTHER_ACTIVITY, UNRECOGNIZED
}
\ No newline at end of file
package com.ypsx.yppos.http.data.entity
enum class ProductPriceType {
//MEMBER_TYPE为会员价,SALE_TYPE为售价,UNRECOGNIZED_TYPE为未知类型
MEMBER_TYPE,SALE_TYPE,UNRECOGNIZED_TYPE
}
\ No newline at end of file
package com.ypsx.yppos.http.data.request
/**
* 申请POS机请求数据
*/
data class ApplyPosMachineRequest(
//品牌
var brand: String,
//IP地址
var ip: String,
//设备mac地址
var macAddress: String,
//通信端口号
var port: Int = 0,
//POS系统版本号
var version: String,
//型号规格
var specification: String,
//安装盘容量
var installDiskCapacity: String ,
//安装目录
var installationDirectory: String
// {
// "brand": "string",
// "installDiskCapacity": "string",
// "installationDirectory": "string",
// "ip": "string",
// "macAddress": "string",
// "port": 0,
// "remark": "string",
// "specification": "string",
// "version": "string"
// }
)
package com.ypsx.yppos.http.data.request
data class InsertOrderPaymentRequest(
var payMethod:String,
var payTime:String,
var total:Double
)
package com.ypsx.yppos.http.data.request
import com.ypsx.yppos.http.data.bean.InsertOrderProductDTO
import com.ypsx.yppos.http.data.bean.OrderActivityResponse
data class InsertOrderRequest(
var activitys: List<OrderActivityResponse>? = null,
var createTime: String,
var creatorId: String,
var creatorName: String,
var einvoiceQrcodeUrl: String? = null,
var memberId: String? = null,
var mobile: String? = null,
var orderNo: String,
var posId: String,
var storeId: String,
var status: String,
var roundPrice: Double = 0.0,
var remark: String? = null,
var products: List<InsertOrderProductDTO>,
//订单应该支付的金额,等于商品行的成交价*数量
var totalAmount: Double,
//商品行特价优惠总金额(不算调订单级优惠)
var promotionOnSaleTotal: Double,
//订单级满减优惠金额
var discountTotal: Double,
// 订单实际支付的金额=totalAmount-discountTotal
var payAmount: Double,
//找零金额
var changeAmount: Double = 0.0,
)
package com.ypsx.yppos.http.data.request
/**
* 强制登出请求数据
*/
data class KickoffRequest(
//手机号
var mobile:String
)
package com.ypsx.yppos.http.data.request
data class PosLoginRequest(
//门店id
var workingOrgId: String,
//账户名称(可以为手机号或邮箱)
var username: String,
//账户密码
var password: String,
//pos机id
var posId: String
)
package com.ypsx.yppos.http.data.request
data class ProductItem(
var merchantItemId:String = "",
var productId:String,
var quantity:Double,
var price:Double,
)
package com.ypsx.yppos.http.data.request
data class ProductPromotionRequest(
//会员id
// var memberId: String,
//门店id
var shopId: String,
//订单号
var orderNo: String,
var products: ArrayList<ProductItem> = arrayListOf()
)
package com.ypsx.yppos.http.data.request
data class ReplyCommandListRequest(
var commandIds: List<String>
)
package com.ypsx.yppos.http.data.request
data class SaveCommandResultRequest(
var commandId: Long,
val elapsedTimeInSeconds: Long,
val result: String,
val success: Boolean
)
package com.ypsx.yppos.http.data.request
data class UnifiedCashPaySignRequest(
var authCode:String,
var orderId:String,
var payMethod:String,
var entry: String? = "ORDER",
var roundPrice:Double=0.0,
var total:Double,
)
package com.ypsx.yppos.http.repository.local
class LocalDataManger {
companion object {
val instance: LocalDataManger by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
LocalDataManger()
}
}
}
\ No newline at end of file
package com.ypsx.yppos.http.state
/**
* 作者 : hegaojian
* 时间 : 2020/3/11
* 描述 :操作数据的状态类
*/
data class UpdateUiState<T>(
//请求是否成功
var isSuccess: Boolean = true,
//操作的对象
var data: T? = null,
//请求失败的错误信息
var errorMsg: String = ""
)
\ No newline at end of file
package com.ypsx.yppos.jobs
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import com.ypsx.base.base.appContext
import com.ypsx.yppos.R
import com.ypsx.yppos.http.repository.request.HttpRequestCoroutine
import com.ypsx.yppos.room.entity.PosConfig
import com.ypsx.yppos.room.repository.PosConfigRepository
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class LogService : Service() {
var posConfig: PosConfig? = null
var job: Job? = null
companion object {
var starter: Intent? = null
fun start(context: Context) {
if (starter == null){
starter = Intent(context, LogService::class.java)
}
context.startService(starter)
}
fun stop(context: Context) {
starter?.let {
context.stopService(starter)
}
}
}
override fun onCreate() {
super.onCreate()
posConfig = PosConfigRepository.getInstance().loadConfig()
job = GlobalScope.launch {
HttpRequestCoroutine.uploadLog(posConfig?.code!!)
}
}
override fun onDestroy() {
super.onDestroy()
job?.cancel()
stopForeground(true)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createEmptyNotification()
return super.onStartCommand(intent, flags, startId)
}
private fun createEmptyNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var channel = NotificationChannel(
"CHANNEL_ID", "CHANNEL_NAME",
NotificationManager.IMPORTANCE_HIGH
)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
val notification =
Notification.Builder(appContext, "CHANNEL_ID")
.setContentTitle("后台服务")
.setSmallIcon(R.drawable.icon_yp_logo)
.build()
startForeground(1, notification)
// startForeground(1, new Notification());
}
}
}
\ No newline at end of file
package com.ypsx.yppos.jobs
import android.app.job.JobParameters
import android.app.job.JobService
class PosCommandJobService : JobService() {
override fun onStartJob(params: JobParameters?): Boolean {
jobFinished(params, true)
return false
}
override fun onStopJob(params: JobParameters?): Boolean {
return false
}
}
\ No newline at end of file
package com.ypsx.yppos.jobs
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import com.ypsx.base.base.appContext
import com.ypsx.yppos.R
import com.ypsx.yppos.http.repository.request.HttpRequestCoroutine
import com.ypsx.yppos.room.entity.PosConfig
import com.ypsx.yppos.room.repository.PosConfigRepository
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class SyncProductService: Service() {
var posConfig: PosConfig? = null
var job: Job? = null
companion object {
var starter: Intent? = null
fun start(context: Context) {
if (starter == null){
starter = Intent(context, SyncProductService::class.java)
}
context.startService(starter)
}
fun stop(context: Context) {
starter?.let {
context.stopService(starter)
}
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
posConfig = PosConfigRepository.getInstance().loadConfig()
job = GlobalScope.launch {
val storeId = posConfig?.storeId!!
HttpRequestCoroutine.syncProduct(storeId)
}
}
override fun onDestroy() {
super.onDestroy()
job?.cancel()
stopForeground(true)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createEmptyNotification()
return super.onStartCommand(intent, flags, startId)
}
private fun createEmptyNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var channel = NotificationChannel(
"CHANNEL_ID", "CHANNEL_NAME",
NotificationManager.IMPORTANCE_HIGH
)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
val notification =
Notification.Builder(appContext, "CHANNEL_ID")
.setContentTitle("后台服务")
.setSmallIcon(R.drawable.icon_yp_logo)
.build()
startForeground(1, notification)
// startForeground(1, new Notification());
}
}
}
\ No newline at end of file
package com.ypsx.yppos.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.ypsx.yppos.room.entity.PosBuy
import com.ypsx.yppos.room.entity.PosConfig
@Dao
interface PosBuyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(posBuy: PosBuy)
@Query("SELECT * FROM POS_Buy ORDER BY id DESC LIMIT 1")
fun queryPosBuy(): PosBuy?
}
\ No newline at end of file
package com.ypsx.yppos.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.ypsx.yppos.room.entity.PosBuyPart
import com.ypsx.yppos.room.entity.PosConfig
@Dao
interface PosBuyPartDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(posBuyPart: PosBuyPart)
@Query("select * from POS_BuyPart Where orderId=:orderNo")
fun queryByOrderNo(orderNo:String):List<PosBuyPart>?
}
\ No newline at end of file
package com.ypsx.yppos.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.ypsx.yppos.room.entity.PosBuyPart
import com.ypsx.yppos.room.entity.PosBuyPayment
import com.ypsx.yppos.room.entity.PosConfig
@Dao
interface PosBuyPaymentDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(posBuyPayment: PosBuyPayment)
@Query("select * from POS_BuyPayment Where orderId=:orderNo")
fun queryByOrderNo(orderNo: String): PosBuyPayment?
}
\ No newline at end of file
package com.ypsx.yppos.room.dao
import androidx.room.*
import com.ypsx.yppos.room.entity.PosConfig
@Dao
interface PosConfigDao {
@Query("SELECT * from POS_Config LIMIT 0,1")
fun loadConfig(): PosConfig?
@Query("SELECT * from POS_Config where id=:id LIMIT 0,1")
fun loadConfig(id:String): PosConfig?
@Query("DELETE FROM POS_Config")
fun deleteAll()
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(posConfig: PosConfig)
@Update
fun update(posConfig: PosConfig)
}
\ No newline at end of file
package com.ypsx.yppos.room.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.ypsx.yppos.room.entity.PosProduct
@Dao
interface PosProductDao {
@Query("SELECT * from POS_Product WHERE (barCode=:key OR productNumber=:key OR mnemonicCode=:key) AND shelvesStatus='ON' LIMIT 0,1")
fun queryKeyWords(key: String): PosProduct?
@Query("SELECT * from POS_Product WHERE mnemonicCode=:mnemonicCode AND shelvesStatus='ON' LIMIT 0,1")
fun queryByMnemonicCode(mnemonicCode: String): PosProduct?
@Query("SELECT * from POS_Product WHERE id IN (:productId)")
fun queryAllByProductId(productId: ArrayList<String>): List<PosProduct>
@Query("SELECT id from POS_Product WHERE barCode=:key OR productNumber=:key OR mnemonicCode=:key LIMIT 0,1")
fun queryKeyWords2(key: String): String?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg list: PosProduct?)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(posProduct: PosProduct?)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertList(list: List<PosProduct>?)
}
\ No newline at end of file
package com.ypsx.yppos.room.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.ypsx.yppos.room.dao.*
import com.ypsx.yppos.room.entity.*
@Database(
entities = [PosConfig::class, PosProduct::class,
PosBuy::class, PosBuyPart::class, PosBuyPayment::class], version = 1,exportSchema = false
)
abstract class PosDatabase : RoomDatabase() {
abstract fun posConfigDao(): PosConfigDao
abstract fun posProductDao(): PosProductDao
abstract fun posBuyDao(): PosBuyDao
abstract fun posBuyPaymentDao(): PosBuyPaymentDao
abstract fun posBuyPartDao(): PosBuyPartDao
companion object {
@Volatile
private var INSTANCE: PosDatabase? = null
val name:String = "yp_pos.db"
fun getDatabase(context: Context): PosDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
PosDatabase::class.java,
"yp_pos"
).allowMainThreadQueries()
.fallbackToDestructiveMigration().build()
INSTANCE = instance
return instance
}
}
}
}
\ No newline at end of file
package com.ypsx.yppos.room.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* 订单详情
*/
@Entity(tableName = "POS_Buy")
data class PosBuy(
@PrimaryKey
var id: String,
var orderNo: String,
var posId: String,
var changeAmount: Double = 0.00,
var payAmount: Double = 0.00,
var discountTotal: Double = 0.00,
var promotionOnSaleTotal: Double = 0.00,
var totalAmount: Double = 0.00,
var roundPrice: Double = 0.00,
var type:String,
var buyNumber:String,
var remark: String? = null,
var einvoiceQrcodeUrl: String = "",
var status: String,
var storeId: String,
var creatorId: String? = null,
var createTime: String? = null,
var isUpload: Boolean = false,
var creatorName: String? = null,
var activitys: String? = null,
var mobile: String? = null,
var memberId: String? = null
)
package com.ypsx.yppos.room.entity
import androidx.room.*
/**
* 付款商品,包含删除商品
*/
@Entity(
tableName = "POS_BuyPart", indices=[Index(value = ["id","orderId"])], foreignKeys = [ForeignKey(
entity = PosBuy::class,
parentColumns = ["id"],
childColumns = ["orderId"],
onDelete = ForeignKey.CASCADE
)]
)
data class PosBuyPart(
@PrimaryKey
var id: String,
var productId: String,
@ColumnInfo(index = true)
var orderId: String,
var quantity: Double = 0.00,
var tailAmount: Int = 0,
var amount: Double = 0.0,
var weightCode: String? = null,
//是否是删除项
var deleted: Boolean,
var conversionRatio: String? = null,
var name: String,
var spec: String,
var unit: String,
var productNumber: String,
var eaQty: Int,
var barCode: String,
var isVirtual: Boolean,
var activitys: String? = null,
var cardNumber: String? = null,
var style: String? = null,
var pluLevel: String? = null,
var activityType: String? = null,
var payApportionAmount: Double = 0.00,
var activityPrice: Double = 0.00,
var price: Double = 0.00,
var settlementPrice: Double = 0.00,
var discountTotal: Double = 0.00,
var discountPrice: Double = 0.00,
)
package com.ypsx.yppos.room.entity
import androidx.room.*
import androidx.room.ForeignKey.CASCADE
/**
* 付款方式
*/
@Entity(
tableName = "POS_BuyPayment",
indices=[Index(value = ["id","orderId"])],
foreignKeys = [ForeignKey(
entity = PosBuy::class,
parentColumns = ["id"],
childColumns = ["orderId"],
onDelete = CASCADE
)]
)
data class PosBuyPayment(
@PrimaryKey
var id: String,
@ColumnInfo(index = true)
var orderId: String,
var payMethod: String,
var payTime: String,
var total: Double = 0.00,
var payCode: String? = null,
//支付方式
var realPayMethod: String? = null,
var realPayFlowNo: String? = null,
var payFlowNo: String,
var status: String,
var openId: String,
)
package com.ypsx.yppos.room.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "POS_Config")
data class PosConfig(
@PrimaryKey
var id: String,
var posId: String? =null,
var brand: String,
var code: String?=null,
var createTime: String,
var creatorId: String,
var creatorName: String,
var specification:String,
var installDiskCapacity: String,
var installationDirectory: String,
var ip: String,
var macAddress: String,
var orgId: String,
var remark: String? = null,
var storeId: String?=null,
var storeName: String? =null,
var port: Int,
var status:String,
var version:String,
//二维码前链接
var einvoiceQrcodeUrlPrefix:String? =null,
var storeClerkRoleName:String?=null,
var storeManagerRoleName:String?=null,
var isAutoOpen:Boolean=false,
var posCode:String?=null,
var lastestPartUpdateTime:String?=null,
var ZTposId:Int =0,
//删除挂单 0:全部允许 1:需店长授权
var deleteList:Int =0,
//整单取消授权方式: 0: 全部允许, 1:需店长授权
var cancelWholeOrder: Int =0,
//现金圆整方式: 0: 四舍五入
var cashRoundMode: Int=0,
//锁屏时间,单位分钟, =0表示不锁屏,大于0表示锁屏时间(限制5-60)
var lockScreen: Int=0,
//最大挂单数: 限制0-5
var maxPendingOrder: Int=0,
var payMethods: String?=null,
//是否打印小票条码
var printTicketBar: Boolean = false,
//是否打印电子发票
var printTicketQrcode: Boolean =false,
var printTicketQrcodePrompt: String?=null,
//小票底部文字
var ticketBottom:String? =null,
)
package com.ypsx.yppos.room.entity
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "POS_Product")
data class PosProduct(
@PrimaryKey
var id: String,
var barCode: String,
var productNumber: String,
var sellPrice: Double = 0.00,
var balance: Double = 0.00,
//POS门店商品状态
var shelvesStatus: String,
var name: String = "",
var status: String? = null,
//是否是虚拟产品
var isVirtual: Boolean = false,
var style: String,
//单位
var unit: String,
var spec: String,
var storeId: String = "",
var pluLevel: String = "",
//换算比例
var conversionRatio: String? = null,
var merchantItemId: String? = null,
var mnemonicCode: String = ""
)
package com.ypsx.yppos.room.repository
import com.ypsx.base.base.appContext
import com.ypsx.yppos.http.data.bean.OrderDetailsResponse
import com.ypsx.yppos.room.dao.PosBuyDao
import com.ypsx.yppos.room.dao.PosBuyPartDao
import com.ypsx.yppos.room.dao.PosBuyPaymentDao
import com.ypsx.yppos.room.database.PosDatabase
import com.ypsx.yppos.room.entity.PosBuy
import com.ypsx.yppos.room.entity.PosBuyPart
import com.ypsx.yppos.room.entity.PosBuyPayment
class PosBuyRepository {
companion object {
private val instance: PosBuyRepository = PosBuyRepository()
fun getInstance(): PosBuyRepository {
return instance
}
}
private fun getPosBuyPartDao(): PosBuyPartDao {
return PosDatabase.getDatabase(appContext).posBuyPartDao()
}
private fun getPosBuyDao(): PosBuyDao {
return PosDatabase.getDatabase(appContext).posBuyDao()
}
private fun getPosBuyPaymentDao(): PosBuyPaymentDao {
return PosDatabase.getDatabase(appContext).posBuyPaymentDao()
}
fun savePosBuy(orderResponse: OrderDetailsResponse, authCode: String) {
val isEmpty = orderResponse.activitys?.isEmpty() ?: true
val activitys = if (isEmpty) "" else orderResponse.activitys.toString()
val posBuy = PosBuy(
id = orderResponse.id,
orderNo = orderResponse.orderNo,
posId = orderResponse.posId,
buyNumber = orderResponse.buyNumber,
changeAmount = orderResponse.changeAmount,
payAmount = orderResponse.payAmount,
discountTotal = orderResponse.discountTotal,
promotionOnSaleTotal = orderResponse.promotionOnSaleTotal,
totalAmount = orderResponse.totalAmount,
roundPrice = orderResponse.roundPrice,
remark = "",
einvoiceQrcodeUrl = orderResponse.einvoiceQrcodeUrl,
status = orderResponse.status,
storeId = orderResponse.storeId,
creatorId = orderResponse.creatorId,
createTime = orderResponse.createTime,
creatorName = orderResponse.creatorName,
isUpload = true,
activitys = activitys,
type = orderResponse.type,
mobile = orderResponse.mobile,
memberId = orderResponse.memberId
)
getPosBuyDao().insert(posBuy)
for (payment in orderResponse.payments) {
payment.payCode = authCode
getPosBuyPaymentDao().insert(payment)
}
for (product in orderResponse.products) {
val isEmpty1 = product.activitys?.isEmpty() ?: true
val activity = if (isEmpty1) "" else product.activitys.toString()
val activityType = if (isEmpty1) "" else product.activitys?.get(0)?.activityType
val posBuyPart = PosBuyPart(
id = product.id,
orderId = orderResponse.id,
productId = product.productId,
quantity = product.paymentProductQuantity,
barCode = product.barCode,
tailAmount = product.tailAmount,
amount = product.amount,
weightCode = product.weightCode,
deleted = false,
conversionRatio = product.conversionRatio,
name = product.paymentProductName,
price = product.price,
activityPrice = product.activityPrice,
activitys = activity,
eaQty = 0,
spec = product.spec,
unit = product.unit,
style = product.style,
pluLevel = product.pluLevel,
activityType = activityType,
productNumber = product.productNumber,
isVirtual = product.isVirtual,
discountTotal = product.discountTotal,
payApportionAmount = product.payApportionAmount,
)
getPosBuyPartDao().insert(posBuyPart)
}
for (deleteProduct in orderResponse.deleteProducts) {
deleteProduct.deleted = true
deleteProduct.orderId = orderResponse.id
getPosBuyPartDao().insert(deleteProduct)
}
}
fun query(): PosBuy? {
return getPosBuyDao().queryPosBuy()
}
fun queryPosBuyPartByOrderNo(orderNo: String): List<PosBuyPart>? {
return getPosBuyPartDao().queryByOrderNo(orderNo)
}
fun queryPosBuyPaymentByOrderNo(orderNo: String): PosBuyPayment? {
return getPosBuyPaymentDao().queryByOrderNo(orderNo)
}
// fun initData(orderNo: String, posId: String,storeId:String,) {
// var creatorId:String =CacheUtil.getLoginResponse().loginUser.creatorId
// var creatorName:String =CacheUtil.getLoginResponse().loginUser.creatorName
//
// }
}
\ No newline at end of file
package com.ypsx.yppos.room.repository
import androidx.lifecycle.LiveData
import com.ypsx.base.base.appContext
import com.ypsx.yppos.http.data.bean.PosConfigResponse
import com.ypsx.yppos.http.data.bean.PosMachineResponse
import com.ypsx.yppos.room.dao.PosConfigDao
import com.ypsx.yppos.room.database.PosDatabase
import com.ypsx.yppos.room.entity.PosConfig
class PosConfigRepository private constructor() {
companion object {
private val instance: PosConfigRepository = PosConfigRepository()
fun getInstance(): PosConfigRepository {
return instance
}
}
private fun getPosConfigDao(): PosConfigDao {
return PosDatabase.getDatabase(appContext).posConfigDao()
}
fun loadConfig(): PosConfig? {
return getPosConfigDao().loadConfig()
}
fun savePosDetail(posMachine: PosMachineResponse) {
val id = posMachine.id
var posConfig = getPosConfigDao().loadConfig(id)
if (posConfig == null) {
posConfig = PosConfig(
id,
posId = posMachine.posId,
brand = posMachine.brand,
createTime = posMachine.createTime,
port = posMachine.port,
code = posMachine.code,
creatorId = posMachine.updatorId,
creatorName = posMachine.updatorName,
installDiskCapacity = posMachine.installDiskCapacity,
installationDirectory = posMachine.installationDirectory,
ip = posMachine.ip,
macAddress = posMachine.macAddress,
orgId = posMachine.orgId,
remark = posMachine.remark,
storeId = posMachine.storeId,
storeName = posMachine.storeName,
status = posMachine.status,
version = posMachine.version,
specification = posMachine.specification
)
getPosConfigDao().insert(posConfig)
} else {
posConfig.status = posMachine.status
posConfig.posId = posMachine.posId
posConfig.code = posMachine.code
posConfig.brand = posMachine.brand
posConfig.createTime = posMachine.createTime
posConfig.port = posMachine.port
posConfig.creatorId = posMachine.updatorId
posConfig.creatorName = posMachine.updatorName
posConfig.installDiskCapacity = posMachine.installDiskCapacity
posConfig.installationDirectory = posMachine.installationDirectory
posConfig.ip = posMachine.ip
posConfig.macAddress = posMachine.macAddress
posConfig.orgId = posMachine.orgId
posConfig.remark = posMachine.remark
posConfig.storeId = posMachine.storeId
posConfig.storeName = posMachine.storeName
posConfig.version = posMachine.version
posConfig.specification = posMachine.specification
getPosConfigDao().update(posConfig)
}
}
fun savePosConfig(id: String, config: PosConfigResponse) {
var posConfig = getPosConfigDao().loadConfig(id)
posConfig!!.ticketBottom = config.ticketBottom
//是否打印电子发票
posConfig.printTicketQrcode = config.printTicketQrcode
//是否打印小票条码
posConfig.printTicketBar = config.printTicketBar
//付款方式,多个值以英文逗号分隔
posConfig.payMethods = config.payMethods
//删除挂单 0:全部允许 1:需店长授权
posConfig.deleteList = config.deleteList
//整单取消授权方式: 0: 全部允许, 1:需店长授权
posConfig.cancelWholeOrder = config.cancelWholeOrder
//现金圆整方式: 0: 四舍五入
posConfig.cashRoundMode = config.cashRoundMode
//锁屏时间,单位分钟, =0表示不锁屏,大于0表示锁屏时间(限制5-60)
posConfig.lockScreen = config.lockScreen
//最大挂单数: 限制0-5
posConfig.maxPendingOrder = config.maxPendingOrder
posConfig.storeManagerRoleName = config.options["storeManagerRoleName"]
posConfig.einvoiceQrcodeUrlPrefix = config.options["einvoiceQrcodeUrlPrefix"]
posConfig.storeClerkRoleName = config.options["storeClerkRoleName"]
getPosConfigDao().update(posConfig)
}
}
\ No newline at end of file
package com.ypsx.yppos.room.repository
import com.ypsx.base.base.appContext
import com.ypsx.yppos.room.dao.PosConfigDao
import com.ypsx.yppos.room.dao.PosProductDao
import com.ypsx.yppos.room.database.PosDatabase
import com.ypsx.yppos.room.entity.PosProduct
import com.ypsx.yppos.utils.dXLog
class PosProductRepository {
companion object {
private val instance: PosProductRepository = PosProductRepository()
fun getInstance(): PosProductRepository {
return instance
}
}
private fun getPosProductDao(): PosProductDao {
return PosDatabase.getDatabase(appContext).posProductDao()
}
fun insertAll(posProduct: List<PosProduct>) {
getPosProductDao().insertList(posProduct)
}
fun queryKeyWords(key: String): PosProduct? {
return getPosProductDao().queryKeyWords(key)
}
fun queryByMnemonicCode(mnemonicCode:String):PosProduct?{
return getPosProductDao().queryByMnemonicCode(mnemonicCode)
}
fun queryId(key: String): String? {
return getPosProductDao().queryKeyWords2(key)
}
fun queryAllIn( productIds: ArrayList<String>):List<PosProduct> {
productIds.toArray().toString().dXLog()
return getPosProductDao().queryAllByProductId(productIds)
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.activity
import android.os.Bundle
import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import androidx.activity.viewModels
import com.ypsx.base.ext.view.afterTextChange
import com.ypsx.base.ext.view.textStringTrim
import com.ypsx.base.network.manager.NetState
import com.ypsx.base.util.RegexUtils
import com.ypsx.yppos.R
import com.ypsx.yppos.app.base.BaseActivity
import com.ypsx.yppos.app.ext.startKtxActivityFinish
import com.ypsx.yppos.databinding.ActivityLoginBinding
import com.ypsx.yppos.ui.popup.AppUpdaterPopupWindow
import com.ypsx.yppos.ui.popup.MessagePopupWindow
import com.ypsx.yppos.utils.CacheUtil
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.utils.toast
import com.ypsx.yppos.viewmodel.request.RequestLoginViewModel
import com.ypsx.yppos.viewmodel.state.LoginViewModel
/**
* 登录页
*/
class LoginActivity : BaseActivity<LoginViewModel, ActivityLoginBinding>() {
private val requestLoginViewModel: RequestLoginViewModel by viewModels()
override fun layoutId(): Int = R.layout.activity_login
override fun initView(savedInstanceState: Bundle?) {
mDatabind.vm = mViewModel
mDatabind.click = ProxyClick()
mDatabind.etAccount.afterTextChange {
val a = it.isNotEmpty() && RegexUtils.isMobileSimple(it)
val password = mDatabind.etPassword.textStringTrim()
val b = password.isNotEmpty() && password.length >= 6
if (a && b) {
mViewModel.enabledStatus.set(true)
} else {
mViewModel.enabledStatus.set(false)
}
}
mDatabind.etPassword.afterTextChange {
val b = it.isNotEmpty() && it.length >= 6
val username = mDatabind.etAccount.textStringTrim()
val a = username.isNotEmpty() && RegexUtils.isMobileSimple(username)
if (a && b) {
mViewModel.enabledStatus.set(true)
} else {
mViewModel.enabledStatus.set(false)
}
}
CacheUtil.getAliYunKey().dXLog()
requestLoginViewModel.upgradeApk(mViewModel.posId.get())
}
inner class ProxyClick {
fun login() {
val username = mDatabind.etAccount.textStringTrim()
val password = mDatabind.etPassword.textStringTrim()
showLoading(getString(R.string.loading))
requestLoginViewModel.login(username, password)
}
fun eye() {
val isHideFirst = mViewModel.isHideFirst.get()
if (isHideFirst) {
mViewModel.isHideFirst.set(false)
mDatabind.btEye.setImageResource(R.mipmap.ic_eye_open)
//显示
mDatabind.etPassword.transformationMethod =
HideReturnsTransformationMethod.getInstance()
mDatabind.etPassword.setSelection(mDatabind.etPassword.textStringTrim().length)
} else {
mViewModel.isHideFirst.set(true)
mDatabind.etPassword.transformationMethod =
PasswordTransformationMethod.getInstance()
mDatabind.btEye.setImageResource(R.mipmap.ic_eye_close)
mDatabind.etPassword.setSelection(mDatabind.etPassword.textStringTrim().length)
}
}
}
override fun createObserver() {
requestLoginViewModel.posDetails.observe(this, {
dismissLoading()
if (it.isSuccess) {
startKtxActivityFinish<MainActivity>()
}
})
requestLoginViewModel.posDetails2.observe(this, {
dismissLoading()
if (!it.isSuccess) {
val messagePopupWindow = MessagePopupWindow(this)
messagePopupWindow.showPopupWindow()
messagePopupWindow.setData("提示", it.errorMsg, "取消", "确定")
messagePopupWindow.setMessageClick {
val username = mDatabind.etAccount.textStringTrim()
showLoading(getString(R.string.loading))
requestLoginViewModel.posKickOff(username)
}
} else {
it.errorMsg.toast()
}
})
requestLoginViewModel.appVersion.observe(this,{
val appUpdaterPopupWindow = AppUpdaterPopupWindow(this)
appUpdaterPopupWindow.showPopupWindow()
appUpdaterPopupWindow.setData(it)
})
}
override fun onNetworkStateChanged(netState: NetState) {
if (netState.isSuccess) {
"我特么终于有网了啊!".dXLog()
} else {
"我特么怎么断网了!".dXLog()
}
}
}
\ No newline at end of file
This diff is collapsed.
package com.ypsx.yppos.ui.activity
import android.os.Bundle
import androidx.activity.viewModels
import com.elvishew.xlog.XLog
import com.ypsx.base.ext.util.toJson
import com.ypsx.base.network.manager.NetState
import com.ypsx.base.util.AppUtils
import com.ypsx.base.util.DeviceUtils
import com.ypsx.base.util.LogUtils
import com.ypsx.yppos.R
import com.ypsx.yppos.app.base.BaseActivity
import com.ypsx.yppos.app.ext.startKtxActivityFinish
import com.ypsx.yppos.databinding.ActivityRegisterBinding
import com.ypsx.yppos.http.data.entity.PosStatus
import com.ypsx.yppos.http.data.request.ApplyPosMachineRequest
import com.ypsx.yppos.ui.popup.MessagePopupWindow
import com.ypsx.yppos.utils.CacheUtil
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.utils.toast
import com.ypsx.yppos.viewmodel.request.RequestRegisterViewModel
import com.ypsx.yppos.viewmodel.state.RegisterViewModel
/**
* 注册页
*/
class RegisterActivity : BaseActivity<RegisterViewModel, ActivityRegisterBinding>() {
private val requestMeViewModel: RequestRegisterViewModel by viewModels()
override fun layoutId(): Int = R.layout.activity_register
override fun initView(savedInstanceState: Bundle?) {
mDatabind.vm = mViewModel
mDatabind.click = ProxyClick()
}
inner class ProxyClick {
fun submit() {
mViewModel.submitState.set(false)
mViewModel.submitText.set("审核中")
val brand = mViewModel.brand
val model = mViewModel.model
val ipAddress = mViewModel.ipAddress
var port = mDatabind.etPort.text.toString()
if (port.isEmpty()) {
port = mDatabind.etPort.hint.toString()
}
val version = AppUtils.getVersionCode().toString()
val macAddress = DeviceUtils.getMacAddress()
val applyPosMachineRequest = ApplyPosMachineRequest(
brand,
ipAddress,
macAddress,
port.toInt(),
version,
model,
"8G", "/",
)
XLog.d("提交申请信息:${applyPosMachineRequest.toJson()}")
// showLoading("请求网络中...")
requestMeViewModel.applyPos(applyPosMachineRequest)
}
}
override fun createObserver() {
requestMeViewModel.posDetails.observe(this, { data ->
val id = data.id
val status = data.status
requestMeViewModel.savePosConfig(data)
when (status) {
PosStatus.APPLY.name -> {
LogUtils.debugInfo(status)
requestMeViewModel.getById(id)
}
PosStatus.ACTIVE.name -> {
startLogin()
}
PosStatus.APPLY_FAILED.name -> {
val applyFailedMessage = data.applyFailedMessage
LogUtils.debugInfo("错误原因:${applyFailedMessage}")
showError("提示", "您的审核未通过,请重新提交", "我知道了")
}
PosStatus.DISABLED.name -> {
val messagePopupWindow = MessagePopupWindow(this)
messagePopupWindow.showPopupWindow()
messagePopupWindow.setData("提示", "该POS机已停用,是否将删除本地数据", "取消", "确定", false)
messagePopupWindow.setMessageClick {
CacheUtil.setLoginResponse(null)
}
}
}
})
}
private fun startLogin() {
startKtxActivityFinish<LoginActivity>()
}
private fun showError(title: String, content: String, sure: String) {
val messagePopupWindow = MessagePopupWindow(this)
messagePopupWindow.showPopupWindow()
messagePopupWindow.setData(title, content, sure = sure, gone = false)
}
override fun onNetworkStateChanged(netState: NetState) {
super.onNetworkStateChanged(netState)
if (netState.isSuccess) {
"我特么终于有网了啊!".dXLog()
} else {
"我特么怎么断网了!".dXLog()
}
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.activity
import android.Manifest.permission.*
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.activity.viewModels
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import com.ypsx.base.base.viewmodel.BaseViewModel
import com.ypsx.base.ext.parseState
import com.ypsx.base.network.manager.NetState
import com.ypsx.yppos.R
import com.ypsx.yppos.app.base.BaseActivity
import com.ypsx.yppos.app.ext.startKtxActivityFinish
import com.ypsx.yppos.databinding.ActivitySplashBinding
import com.ypsx.yppos.http.data.entity.PosStatus
import com.ypsx.yppos.room.repository.PosConfigRepository
import com.ypsx.yppos.ui.popup.MessagePopupWindow
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.utils.toast
import com.ypsx.yppos.viewmodel.request.RequestSplashViewModel
/**
* 启动也
*/
class SplashActivity : BaseActivity<BaseViewModel, ActivitySplashBinding>() {
private val PERMISSION_REQUEST_CODE = 0 // 系统权限管理页面的参数
private val perms = arrayOf(
READ_PHONE_STATE,
READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE
)
private val requestSplashViewModel: RequestSplashViewModel by viewModels()
// 是否需要系统权限检测
var isRequireCheck = false
override fun layoutId(): Int = R.layout.activity_splash
override fun initView(savedInstanceState: Bundle?) {
isRequireCheck = true
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
isRequireCheck = true
allPermissionsGranted()
} else {
isRequireCheck = false
showMissingPermissionDialog()
}
}
private fun showMissingPermissionDialog() {
val messagePopupWindow = MessagePopupWindow(this)
messagePopupWindow.showPopupWindow()
messagePopupWindow.setData(
"帮助",
"当前应用缺少必要权限。\n\n请点击\"设置\"-\"权限\"-打开所需权限。",
cancel = "退出",
sure = "设置",
true
)
messagePopupWindow.setMessageClick {
startAppSettings()
}
}
override fun onResume() {
super.onResume()
if (isRequireCheck) {
if (lacksPermissions(perms)) {
ActivityCompat.requestPermissions(
this,
perms,
PERMISSION_REQUEST_CODE
)
} else {
allPermissionsGranted() // 全部权限都已获取
}
} else {
isRequireCheck = true
}
}
//全部权限以获取
private fun allPermissionsGranted() {
val posConfig = PosConfigRepository.getInstance().loadConfig()
if (posConfig == null) {
"配置信息为空".dXLog()
startKtxActivityFinish<RegisterActivity>()
} else {
val id = posConfig.id
requestSplashViewModel.getById(id)
}
}
override fun onNetworkStateChanged(netState: NetState) {
super.onNetworkStateChanged(netState)
if (netState.isSuccess) {
"我特么终于有网了啊!".dXLog()
} else {
"我特么怎么断网了!".dXLog()
}
}
// 判断权限集合
private fun lacksPermissions(permissions: Array<String>): Boolean {
for (permission in permissions) {
if (lacksPermission(permission)) {
return true
}
}
return false
}
// 判断是否缺少权限
private fun lacksPermission(permission: String): Boolean {
return ContextCompat.checkSelfPermission(this, permission) ==
PackageManager.PERMISSION_DENIED
}
private fun hasAllPermissionsGranted(grantResults: IntArray): Boolean {
for (grantResult in grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return false
}
}
return true
}
private fun startAppSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:$packageName")
startActivity(intent)
}
override fun createObserver() {
requestSplashViewModel.posDetails.observe(this, { data ->
val status = data.status
requestSplashViewModel.savePosConfig(data)
when (status) {
PosStatus.APPLY.name -> {
// startLogin()
showError("提示", getString(R.string.pos_apply), getString(R.string.see))
}
PosStatus.ACTIVE.name -> {
startLogin()
}
PosStatus.APPLY_FAILED.name -> {
val applyFailedMessage = data.applyFailedMessage
"错误原因:${applyFailedMessage}".dXLog()
showError("提示", getString(R.string.pos_failed), getString(R.string.see))
}
PosStatus.DISABLED.name -> {
showError("提示", getString(R.string.pos_disabled), getString(R.string.see))
}
}
})
}
private fun startLogin() {
startKtxActivityFinish<LoginActivity>()
// startActivity(Intent(this, LoginActivity::class.java))
// finish()
}
private fun showError(title: String, content: String, sure: String) {
val messagePopupWindow = MessagePopupWindow(this)
messagePopupWindow.showPopupWindow()
messagePopupWindow.setData(title, content, sure = sure, gone = false)
messagePopupWindow.setMessageClick {
}
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.adapter
import android.graphics.Color
import android.text.SpannableStringBuilder
import android.text.Spanned
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.ypsx.yppos.R
import com.ypsx.yppos.http.data.bean.InsertOrderProductDTO
import com.ypsx.yppos.http.data.entity.PosProductStyle
import com.ypsx.yppos.utils.RoundBackgroundColorSpan
class OrderProductAdapter(data: ArrayList<InsertOrderProductDTO>) :
BaseQuickAdapter<InsertOrderProductDTO, BaseViewHolder>(
R.layout.item_pos_product, data
) {
override fun convert(holder: BaseViewHolder, item: InsertOrderProductDTO) {
item.run {
holder.setText(R.id.tv_barCode, "条码:$barCode")
if (discountTotal > 0.0) {
val ssb = SpannableStringBuilder("促销 $name")
var roundBackgroundColorSpan =
RoundBackgroundColorSpan(
14, 8, 2,
Color.WHITE,
Color.parseColor("#FF4400")
)
ssb.setSpan(roundBackgroundColorSpan, 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
holder.setText(R.id.tv_name, ssb)
} else {
holder.setText(R.id.tv_name, name)
}
holder.setText(R.id.tv_price, "¥$price/$unit")
holder.setText(R.id.tv_amount, "$amount")
when (style) {
PosProductStyle.SINGLE.name -> {
holder.setGone(R.id.ll_count, false)
holder.setGone(R.id.tv_weight, true)
holder.setText(R.id.tv_count, "${quantity.toInt()}")
}
PosProductStyle.WEIGHT.name -> {
holder.setGone(R.id.ll_count, true)
holder.setGone(R.id.tv_weight, false)
holder.setText(R.id.tv_weight, "$quantity$unit")
}
else -> {
}
}
}
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.dialog
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.Window
import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialog
import androidx.databinding.DataBindingUtil
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.DialogMessageBinding
import com.ypsx.yppos.viewmodel.state.MessageViewModel
class MessageDialog(context: Context?) : AppCompatDialog(context) {
init {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
}
private var viewDataBinding: DialogMessageBinding? = null
var listener: OnClickSureListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewDataBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.dialog_message, null, false)
viewDataBinding?.apply {
vm = MessageViewModel()
click = ProxyClick()
}
setContentView(viewDataBinding!!.root)
window!!.attributes.width = WindowManager.LayoutParams.MATCH_PARENT
window!!.attributes.height = WindowManager.LayoutParams.WRAP_CONTENT
window!!.setGravity(Gravity.CENTER)
setCancelable(true)
setCanceledOnTouchOutside(false)
}
fun showData(title: String, content: String, cancel: String?, sure: String?, gone: Boolean = true) {
if (!isShowing) {
show()
}
viewDataBinding?.apply {
vm!!.title.set(title)
vm!!.content.set(content)
vm!!.gone.set(gone)
if (cancel != null) {
vm!!.cancel.set(cancel)
}
if (sure != null) {
vm!!.sure.set(sure)
}
}
}
fun setOnClickSureListener(listener: OnClickSureListener) {
this.listener = listener
}
inner class ProxyClick {
fun cancel() {
dismiss()
listener?.cancel()
}
fun sure() {
dismiss()
listener?.sure()
}
}
}
interface OnClickSureListener {
fun sure()
fun cancel()
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.content.Context
import android.view.Gravity
import android.view.View
import com.king.app.updater.AppUpdater
import com.king.app.updater.UpdateConfig
import com.king.app.updater.callback.UpdateCallback
import com.king.app.updater.http.OkHttpManager
import com.ypsx.base.base.appContext
import com.ypsx.base.ext.util.screenWidth
import com.ypsx.yppos.utils.toast
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupAppUpdaterBinding
import com.ypsx.yppos.http.data.bean.AppVersionResponse
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.viewmodel.state.AppUpdaterViewModel
import razerdp.basepopup.BasePopupWindow
import java.io.File
import kotlin.math.roundToInt
class AppUpdaterPopupWindow(context: Context?) : BasePopupWindow(context) {
var bind: PopupAppUpdaterBinding? = null
lateinit var appVersionName:String
lateinit var downloadUrl:String
var versionCode: Int = 0
var mAppUpdater: AppUpdater? = null
init {
setContentView(R.layout.popup_app_updater)
width = appContext.screenWidth / 4 * 3
setPopupGravity(GravityMode.RELATIVE_TO_ANCHOR, Gravity.CENTER)
isOutSideTouchable = false
setOutSideDismiss(false)
}
override fun onViewCreated(contentView: View) {
bind = PopupAppUpdaterBinding.bind(contentView)
bind?.vm = AppUpdaterViewModel()
bind?.click = ProxyClick()
}
fun setData(appVersion: AppVersionResponse) {
downloadUrl = appVersion.downloadUrl
appVersionName = appVersion.versionName
versionCode = appVersion.versionCode
bind?.vm?.appVersion?.set("V${appVersion.versionName}")
when (appVersion.mode) {
"RECOMMEND","SILENT" -> {//推荐升级和静默升级
bind?.vm?.appCancel?.set(true)
}
"FORCE" -> {//强制升级
bind?.vm?.appCancel?.set(false)
}
}
}
override fun onBackPressed(): Boolean {
return false
}
inner class ProxyClick {
fun close() {
dismiss()
if (mAppUpdater != null) {
mAppUpdater = null
}
}
fun updater() {
bind?.vm?.updaterShow?.set(false)
val config = UpdateConfig()
config.url = downloadUrl
config.channelId = "100"
config.channelName = "更新下载"
config.versionCode =versionCode
config.notificationIcon = R.mipmap.icon_yp_logo
mAppUpdater = AppUpdater(context, config)
.setHttpManager(OkHttpManager.getInstance())
.setUpdateCallback(object : UpdateCallback {
override fun onDownloading(isDownloading: Boolean) {
if (isDownloading) {
("已经在下载中,请勿重复下载。").toast()
}
}
override fun onStart(url: String) {
bind?.vm?.progressBarShow?.set(true)
bind?.progressBar?.progress = 0
}
override fun onProgress(progress: Long, total: Long, isChange: Boolean) {
if (isChange) {
val currProgress = (progress * 1.0f / total * 100.0f).roundToInt()
bind?.progressBar?.progress = currProgress
}
}
override fun onFinish(file: File) {
dismiss()
"APP下载地址:${file.absolutePath}".dXLog()
}
override fun onError(e: Exception) {
}
override fun onCancel() {
}
})
mAppUpdater?.start()
}
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.content.Context
import android.view.Gravity
import android.view.View
import com.ypsx.base.base.appContext
import com.ypsx.base.ext.util.screenWidth
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupMessageBinding
import com.ypsx.yppos.http.data.entity.PosPayMethod
import com.ypsx.yppos.viewmodel.state.MessageViewModel
import razerdp.basepopup.BasePopupWindow
class MessagePopupWindow(context: Context) : BasePopupWindow(context) {
var binding: PopupMessageBinding?=null
private var messageAction: () -> Unit = { }
init {
setContentView(R.layout.popup_message)
width = appContext.screenWidth / 4 * 3
popupGravity = Gravity.CENTER
isOutSideTouchable = false
setOutSideDismiss(false)
}
override fun onViewCreated(contentView: View) {
binding = PopupMessageBinding.bind(contentView)
binding?.vm = MessageViewModel()
binding?.click = ProxyClick()
}
fun setData(
title: String,
content: String,
cancel: String = "取消",
sure: String = "确定",
gone: Boolean = true
) {
binding?.apply {
vm?.title?.set(title)
vm?.content?.set(content)
vm?.gone?.set(gone)
vm?.cancel?.set(cancel)
vm?.sure?.set(sure)
}
}
inner class ProxyClick {
fun cancel() {
dismiss()
// listener?.cancel()
}
fun sure() {
dismiss()
messageAction.invoke()
// listener?.sure()
}
}
fun setMessageClick(payAction: () -> Unit) {
this.messageAction = payAction
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.content.Context
import android.view.Gravity
import android.view.View
import android.view.animation.Animation
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupOfferDetailsBinding
import com.ypsx.yppos.databinding.PopupSettingsBinding
import com.ypsx.yppos.utils.CacheUtil
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.viewmodel.state.OfferDetailsViewModel
import com.ypsx.yppos.viewmodel.state.SettingsViewModel
import razerdp.basepopup.BasePopupWindow
import razerdp.util.animation.AnimationHelper
import razerdp.util.animation.TranslationConfig
/**
* 优惠详情
*/
class OfferDetailsPopupWindow(context: Context?) : BasePopupWindow(context) {
private lateinit var bind: PopupOfferDetailsBinding
init {
setContentView(R.layout.popup_offer_details)
setAlignBackgroundGravity(Gravity.BOTTOM)
setAlignBackground(true)
setPopupGravity(GravityMode.RELATIVE_TO_ANCHOR, Gravity.TOP)
"${CacheUtil.getRealName()}查看优惠明细".dXLog()
}
override fun onCreateShowAnimation(): Animation {
return AnimationHelper.asAnimation()
.withTranslation(TranslationConfig.FROM_BOTTOM)
.toShow()
}
override fun onCreateDismissAnimation(): Animation {
return AnimationHelper.asAnimation()
.withTranslation(TranslationConfig.TO_BOTTOM)
.toDismiss()
}
override fun onViewCreated(contentView: View) {
bind = PopupOfferDetailsBinding.bind(contentView)
bind.vm = OfferDetailsViewModel()
}
fun setData(
discount: String,
discountSpecial: String,
discountMoney: String,
discountTime: String
) {
bind.vm!!.discount.set("¥$discount")
bind.vm!!.discountSpecial.set("-¥$discountSpecial")
bind.vm!!.discountMoney.set("-¥$discountMoney")
bind.vm!!.discountTime.set("-¥$discountTime")
}
override fun showPopupWindow() {
super.showPopupWindow()
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.content.Context
import android.view.Gravity
import android.view.View
import android.view.animation.Animation
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupOrderPayAmountBinding
import com.ypsx.yppos.http.data.entity.PosPayMethod
import com.ypsx.yppos.room.entity.PosConfig
import com.ypsx.yppos.utils.CacheUtil
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.viewmodel.state.OrderPayAmountViewModel
import razerdp.basepopup.BasePopupWindow
import razerdp.util.animation.AnimationHelper
import razerdp.util.animation.TranslationConfig
class OrderPayAmountPopupWindow(context: Context) : BasePopupWindow(context) {
var bind: PopupOrderPayAmountBinding? = null
var posConfig: PosConfig? = null
var realName = CacheUtil.getRealName()
private var payAction: (item: PosPayMethod) -> Unit =
{ _: PosPayMethod -> }
init {
setContentView(R.layout.popup_order_pay_amount)
setAlignBackgroundGravity(Gravity.BOTTOM)
"${realName}选择结算".dXLog()
}
override fun onViewCreated(contentView: View) {
bind = PopupOrderPayAmountBinding.bind(contentView)
bind?.vm = OrderPayAmountViewModel()
bind?.click = ProxyClick()
val offline: Boolean = bind?.vm?.scanOffline?.get() == false
val cardPay: Boolean = bind?.vm?.scanCardPay?.get() == false
if (offline && cardPay) {
bind?.rbOffline?.isChecked = true
bind?.rbCardPay?.isChecked = false
} else if (offline && !cardPay) {
bind?.rbOffline?.isChecked = true
} else if (!offline && cardPay) {
bind?.rbCardPay?.isChecked = true
}
}
override fun onCreateShowAnimation(): Animation {
return AnimationHelper.asAnimation()
.withTranslation(TranslationConfig.FROM_BOTTOM)
.toShow()
}
override fun onCreateDismissAnimation(): Animation {
return AnimationHelper.asAnimation()
.withTranslation(TranslationConfig.TO_BOTTOM)
.toDismiss()
}
fun setData(orderNo: String, amount: String) {
bind?.vm?.orderNo?.set(orderNo)
bind?.vm?.payAmount?.set(amount)
}
fun setPayClick(payAction: (item: PosPayMethod) -> Unit) {
this.payAction = payAction
}
inner class ProxyClick {
fun close() {
dismiss()
}
fun pay() {
var checkedRadioButtonId = bind?.radio?.checkedRadioButtonId
val payMethod = when (checkedRadioButtonId) {
R.id.rb_offline -> PosPayMethod.CCB_BAR_PAY
R.id.rbCardPay -> PosPayMethod.CARDPAY
else -> PosPayMethod.CCB_BAR_PAY
}
"${realName}选择${payMethod.name}支付".dXLog()
payAction.invoke(payMethod)
}
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.app.Activity
import android.content.Context
import android.view.Gravity
import android.view.View
import android.view.animation.Animation
import com.ypsx.base.base.appContext
import com.ypsx.base.ext.util.screenWidth
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupPayAmountBinding
import com.ypsx.yppos.http.data.entity.PosPayMethod
import com.ypsx.yppos.utils.CacheUtil
import com.ypsx.yppos.utils.dXLog
import com.ypsx.yppos.viewmodel.state.PayAmountViewModel
import razerdp.basepopup.BasePopupWindow
import razerdp.util.animation.AnimationHelper
import razerdp.util.animation.TranslationConfig
import java.util.*
class PayAmountPopupWindow(context: Context) : BasePopupWindow(context) {
companion object {
var state: Boolean = true
}
var binding: PopupPayAmountBinding? = null
lateinit var activity: Activity
lateinit var payMethod: PosPayMethod
var timer: Timer? = null
var task: TimerTask? = null
var recLen = 60
init {
setContentView(R.layout.popup_pay_amount)
width = appContext.screenWidth / 4 * 3
popupGravity = Gravity.CENTER
}
override fun onViewCreated(contentView: View) {
binding = PopupPayAmountBinding.bind(contentView)
binding?.vm = PayAmountViewModel()
binding?.click = ProxyClick()
activity = context
}
override fun onCreateShowAnimation(): Animation {
return AnimationHelper.asAnimation()
.withTranslation(TranslationConfig.FROM_BOTTOM)
.toShow()
}
override fun onCreateDismissAnimation(): Animation {
return AnimationHelper.asAnimation()
.withTranslation(TranslationConfig.TO_BOTTOM)
.toDismiss()
}
fun setData(payMethod: PosPayMethod, amount: String) {
binding?.vm?.amount?.set(amount)
this.payMethod = payMethod
state = true
var title = ""
when (payMethod) {
PosPayMethod.CCB_BAR_PAY -> {
title = "扫码支付"
binding?.vm?.payTitle?.set("扫码支付")
binding?.vm?.payTips?.set("扫描消费者出示的付款码")
}
PosPayMethod.CARDPAY -> {
title = "会员余额"
binding?.vm?.payTitle?.set("会员余额")
binding?.vm?.payTips?.set("扫描用户出示的会员付款码")
}
else -> {
dismiss()
}
}
"${CacheUtil.getRealName()}打开${title},应收款:¥${amount}".dXLog()
}
@JvmName("getPayMethod1")
fun getPayMethod(): PosPayMethod {
return payMethod
}
fun setShowTimer() {
recLen = 60
state = false
timer = Timer()
binding?.vm?.tipsShow?.set(false)
binding?.vm?.timeText?.set("${recLen}s")
task = object : TimerTask() {
override fun run() {
recLen--
if (recLen < 0) {
state = true
activity.runOnUiThread {
payAction.invoke()
binding?.vm?.timeText?.set("0s")
}
} else {
binding?.vm?.timeText?.set("${recLen}s")
}
if (recLen < 0) {
stopTimer()
}
}
}
timer?.schedule(task, 1000, 1000);
}
private fun stopTimer() {
if (timer != null && task != null) {
timer?.cancel()
timer?.purge()
timer = null
task = null
}
}
fun timeIsFinish(): Boolean {
return recLen <= 0
}
fun timeIsStart(): Boolean {
return binding?.vm?.tipsShow?.get() ?: true
}
override fun dismiss() {
super.dismiss()
stopTimer()
}
private var payAction: () -> Unit =
{ }
fun setFinishClick(payAction: () -> Unit) {
this.payAction = payAction
}
inner class ProxyClick {
fun close() {
dismiss()
}
}
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.content.Context
import android.view.Gravity
import android.view.View
import com.ypsx.base.base.appContext
import com.ypsx.base.ext.util.screenWidth
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupPayFailedBinding
import com.ypsx.yppos.viewmodel.state.PayFailedViewModel
import razerdp.basepopup.BasePopupWindow
class PayFailedPopWindow(context: Context?) : BasePopupWindow(context) {
var bind: PopupPayFailedBinding? = null
var onPayFailedClick: OnPayFailedClick? =null
init {
setContentView(R.layout.popup_pay_failed)
width = appContext.screenWidth / 4 * 3
popupGravity = Gravity.CENTER
isOutSideTouchable = false
setOutSideDismiss(false)
}
override fun onViewCreated(contentView: View) {
bind = PopupPayFailedBinding.bind(contentView)
bind?.vm = PayFailedViewModel()
bind?.click = ProxyClick()
}
fun setData(message: String) {
bind?.vm?.failedMessage?.set(message)
}
@JvmName("setOnPayFailedClick1")
fun setOnPayFailedClick(onPayFailedClick: OnPayFailedClick) {
this.onPayFailedClick = onPayFailedClick
}
inner class ProxyClick {
fun back() {
dismiss()
onPayFailedClick?.back()
}
fun payAgain() {
dismiss()
onPayFailedClick?.payAgain()
}
}
}
interface OnPayFailedClick{
fun back()
fun payAgain()
}
\ No newline at end of file
package com.ypsx.yppos.ui.popup
import android.app.Activity
import android.content.Context
import android.view.Gravity
import android.view.View
import androidx.lifecycle.viewModelScope
import com.ypsx.base.base.appContext
import com.ypsx.base.ext.util.screenWidth
import com.ypsx.yppos.R
import com.ypsx.yppos.databinding.PopupPaySuccessBinding
import com.ypsx.yppos.viewmodel.state.PaySuccessViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import razerdp.basepopup.BasePopupWindow
import java.util.*
class PaySuccessPopWindow(context: Context) : BasePopupWindow(context) {
lateinit var bind: PopupPaySuccessBinding
var timer: Timer? = null
var task: TimerTask? = null
var recLen = 5
lateinit var activity: Activity
init {
setContentView(R.layout.popup_pay_success)
width = appContext.screenWidth / 4 * 3
popupGravity = Gravity.CENTER
isOutSideTouchable = false
setOutSideDismiss(false)
}
private fun stopTimer() {
if (task != null && timer != null) {
timer?.cancel()
timer?.purge()
timer = null
task = null
}
}
override fun onViewCreated(contentView: View) {
bind = PopupPaySuccessBinding.bind(contentView)
bind.vm = PaySuccessViewModel()
bind.click = ProxyClick()
activity = context
timer = Timer()
task = object : TimerTask() {
override fun run() {
recLen--
if (recLen < 0) {
if (isShowing) {
activity.runOnUiThread {
dismiss()
}
}
stopTimer()
}
}
}
timer?.schedule(task, 1000, 1000);
}
fun setData(total: String) {
bind.vm?.amount?.set(total)
}
override fun dismiss() {
super.dismiss()
stopTimer()
}
inner class ProxyClick {
fun close() {
dismiss()
}
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment