diff --git a/.gitignore b/.gitignore index a8b0d1d..aa724b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,15 @@ -# ---> Android -# Gradle files -.gradle/ -build/ - -# Local configuration file (sdk path, etc) -local.properties - -# Log/OS Files -*.log - -# Android Studio generated files and folders -captures/ -.externalNativeBuild/ -.cxx/ -*.apk -output.json - -# IntelliJ *.iml -.idea/ -misc.xml -deploymentTargetDropDown.xml -render.experimental.xml - -# Keystore files -*.jks -*.keystore - -# Google Services (e.g. APIs or Firebase) -google-services.json - -# Android Profiling -*.hprof - +.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 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..66149fe --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..639c779 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b2c751a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..8919852 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,104 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + id("com.google.devtools.ksp") version "2.0.21-1.0.27" +} + +android { + namespace = "com.shuwei.dish.match" + compileSdk = 36 + + defaultConfig { + applicationId = "com.shuwei.dish.match" + minSdk = 29 + targetSdk = 36 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + ndk { + //设置支持的SO库架构 + abiFilters.addAll(listOf("armeabi", "armeabi-v7a")) + //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a' + } + } + signingConfigs { + create("debug_507") { + storeFile = file("../system.keystore") + storePassword = "android" + keyAlias = "wabon" + keyPassword = "android" + } + } + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + signingConfig = signingConfigs.getByName("debug_507") + } + debug { + //507扫码秤主板在android.buildTypes{}内增加 + signingConfig = signingConfigs.getByName("debug_507") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + viewBinding = true + } + packagingOptions { + pickFirst("lib/armeabi-v7a/libserial_port.so") + pickFirst("lib/arme64-v8a/libserial_port.so") + } +} + +dependencies { + implementation(fileTree("libs") { include("*.jar", "*.aar") }) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + + implementation("com.squareup.okhttp3:okhttp:4.9.1") + implementation("com.google.code.gson:gson:2.10.1") +// implementation("com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30") + + implementation("io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.1.2") + + implementation("androidx.room:room-runtime:2.5.2") + ksp("androidx.room:room-compiler:2.5.2") + implementation("androidx.room:room-ktx:2.5.2") // Kotlin扩展 + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7") + implementation("androidx.activity:activity-ktx:1.8.0") + +// implementation("biz.laenger.android:vpbs:0.0.6") + + //必须 1.0.0 以上 + implementation("io.github.scwang90:refresh-layout-kernel:3.0.0-alpha") + //核心必须依赖 + implementation("io.github.scwang90:refresh-header-classics:3.0.0-alpha") + //经典刷新头 + implementation("io.github.scwang90:refresh-header-radar:3.0.0-alpha") + //雷达刷新头 + implementation("io.github.scwang90:refresh-header-falsify:3.0.0-alpha") + //虚拟刷新头 + implementation("io.github.scwang90:refresh-header-material:3.0.0-alpha") + //谷歌刷新头 + implementation("io.github.scwang90:refresh-header-two-level:3.0.0-alpha") + //二级刷新头 + implementation("io.github.scwang90:refresh-footer-ball:3.0.0-alpha") + //球脉冲加载 + implementation("io.github.scwang90:refresh-footer-classics:3.0.0-alpha") + //经典加载 +} \ No newline at end of file diff --git a/app/libs/WbAiThingsSDK.aar b/app/libs/WbAiThingsSDK.aar new file mode 100644 index 0000000..af005b4 Binary files /dev/null and b/app/libs/WbAiThingsSDK.aar differ diff --git a/app/libs/utilcode.jar b/app/libs/utilcode.jar new file mode 100644 index 0000000..4992673 Binary files /dev/null and b/app/libs/utilcode.jar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/release/app-release.apk b/app/release/app-release.apk new file mode 100644 index 0000000..88b8238 Binary files /dev/null and b/app/release/app-release.apk differ diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm new file mode 100644 index 0000000..b6b41bc Binary files /dev/null and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm new file mode 100644 index 0000000..917e1a2 Binary files /dev/null and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..4dd93a2 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,37 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.shuwei.dish.match", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File", + "baselineProfiles": [ + { + "minApi": 28, + "maxApi": 30, + "baselineProfiles": [ + "baselineProfiles/1/app-release.dm" + ] + }, + { + "minApi": 31, + "maxApi": 2147483647, + "baselineProfiles": [ + "baselineProfiles/0/app-release.dm" + ] + } + ], + "minSdkVersionForDexing": 29 +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/shuwei/dish/match/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/shuwei/dish/match/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..7d9a778 --- /dev/null +++ b/app/src/androidTest/java/com/shuwei/dish/match/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.shuwei.dish.match + +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.shuwei.dish.shelves", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d328061 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/img.png b/app/src/main/img.png new file mode 100644 index 0000000..a0858ee Binary files /dev/null and b/app/src/main/img.png differ diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt new file mode 100644 index 0000000..90ab830 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt @@ -0,0 +1,54 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.ListItemDishCookBinding +import com.shuwei.dish.match.entity.CookFoodGoodsEntity + +class DishPartAdapter(list: MutableList) : + BaseQuickAdapter(list) { + + inner class VH(var binding: ListItemDishCookBinding) : QuickViewHolder(binding.root) + + override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { + val inflater = LayoutInflater.from(context) + val binding = ListItemDishCookBinding.inflate(inflater, parent, false) + return VH(binding) + } + + override fun onBindViewHolder(holder: VH, position: Int, item: CookFoodGoodsEntity?) { + holder.binding.run { + tvDishName.text = item!!.goodsName + tvDishType.text = if (item.materialType == 1) "主辅材:主材" else if (item.materialType == 2) "主辅材:辅材" else "" + tvDishWeight.text = + if (item.useWeight == null || item.useWeight == 0) "" else "${item.useWeight}克" + tvDishWeight.setTextColor( + ContextCompat.getColor( + context, + if (item.isSamplingPage) R.color.black999 else R.color.dish_green + ) + ) + ivOperateIcon.setImageResource( + if (item.isSamplingPage) + //采集页面 + R.drawable.ic_delete + else + //制作页面 + if (item.isSetFinished) R.drawable.ic_dish_selected + else R.drawable.ic_dish_unselected + ) + root.run { + setBackgroundResource( + if (item.isItemClicked) R.drawable.shape_item_cook_dish + else R.drawable.shape_white_fb_15_corners + ) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt new file mode 100644 index 0000000..7bf426f --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt @@ -0,0 +1,59 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.databinding.ListItemDishBinding +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.visible + +class DishShowAdapter(list: MutableList) : + BaseQuickAdapter(list) { + + inner class VH(var binding: ListItemDishBinding) : QuickViewHolder(binding.root) + + override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { + val inflater = LayoutInflater.from(context) + val binding = ListItemDishBinding.inflate(inflater, parent, false) + return VH(binding) + } + + override fun onBindViewHolder(holder: VH, position: Int, item: FoodRecord?) { + holder.binding.run { + val isCooking = item!!.isCooking + val totalWeight = item.totalWeight ?: 0 + tvDishName.text = item!!.foodName + if (isCooking) { + tvDishName.setTextColor( + ContextCompat.getColor(holder.itemView.context, R.color.black) + ) + clBlock.setBackgroundResource(R.drawable.shape_white_fb_15_corners) + tvDishCount.visible() + tvDishCount.text = "制作统计:-kg" + tvShowState.text = "烹饪中" + } else if (totalWeight > 0) { + tvDishName.setTextColor( + ContextCompat.getColor(holder.itemView.context, R.color.dish_green) + ) + clBlock.setBackgroundResource(R.drawable.shape_white_fb_15_corners) + tvDishCount.visible() + tvDishCount.text = + "累计统计:${totalWeight / 1000.0F}kg(${item.count}次)" + tvShowState.text = "${(item.foodWeight ?: 0) / 1000.0F}kg" + } else { + tvDishName.setTextColor( + ContextCompat.getColor(holder.itemView.context, R.color.black999) + ) + clBlock.setBackgroundResource(R.drawable.shape_white_dash_15_corners) + tvDishCount.gone() + tvShowState.text = "-" + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt new file mode 100644 index 0000000..7f6e7fd --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt @@ -0,0 +1,33 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.databinding.ListItemDishRecordBinding +import com.shuwei.dish.match.entity.FoodRecord + +class FoodRecordAdapter(list: MutableList) : + BaseQuickAdapter(list) { + + inner class VH(var binding: ListItemDishRecordBinding) : QuickViewHolder(binding.root) + + override fun onBindViewHolder( + holder: VH, + position: Int, + item: FoodRecord? + ) { + holder.binding.tvDishInfo.text = item?.foodName + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): VH { + val inflater = LayoutInflater.from(context) + val binding = ListItemDishRecordBinding.inflate(inflater, parent, false) + return VH(binding) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt new file mode 100644 index 0000000..c4623f1 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt @@ -0,0 +1,49 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding +import com.shuwei.dish.match.entity.CookFoodGoodsEntity + +class GoodsInfoSearchAdapter(private var list: MutableList) : + BaseQuickAdapter(list) { + + override fun onBindViewHolder( + holder: VH, + position: Int, + item: CookFoodGoodsEntity? + ) { + holder.binding.tvGoodsInfo.run { + text = item?.goodsName + setBackgroundResource( + if (item?.isClicked == true) R.drawable.shape_green_stroke + else R.drawable.shape_white_12_corners + ) + setTextColor( + ContextCompat.getColor( + context, + if (item?.isClicked == true) R.color.dish_green + else R.color.black666 + ) + ) + } + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): VH { + val binding = + ListItemSearchGoodsInfoBinding.inflate(LayoutInflater.from(context), parent, false) + return VH(binding) + } + + inner class VH(var binding: ListItemSearchGoodsInfoBinding) : QuickViewHolder(binding.root) + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt new file mode 100644 index 0000000..233651d --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt @@ -0,0 +1,46 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.ListItemHomeModeBinding +import com.shuwei.dish.match.entity.HomeModeBean +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.visible + +class HomeModeAdapter(list: MutableList) : BaseQuickAdapter(list) { + + inner class VH(var binding: ListItemHomeModeBinding) : QuickViewHolder(binding.root) + + override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { + val binding = ListItemHomeModeBinding.inflate(LayoutInflater.from(context), parent, false) + return VH(binding) + } + + override fun onBindViewHolder(holder: VH, position: Int, item: HomeModeBean?) { + holder.binding.run { + tvModeName.text = item!!.modeName + tvModeDesc.text = item.modeDesc + ivModeIcon.setImageResource(item.modeIconId) + tvLockRemind.run { + if (item.isLocked) visible() else gone() + } + var modeNameColor = R.color.mode_name + var modeDescColor = R.color.mode_desc + var rightIconId = R.drawable.ic_arrow_right + if (item.isLocked) { + modeNameColor = R.color.mode_name + modeDescColor = R.color.mode_desc + rightIconId = R.drawable.ic_lock + } + this.ivRightIcon.setImageResource(rightIconId) + tvModeName.setTextColor(ContextCompat.getColor(root.context, modeNameColor)) + tvModeDesc.setTextColor(ContextCompat.getColor(root.context, modeDescColor)) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt new file mode 100644 index 0000000..d234ba6 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt @@ -0,0 +1,52 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.databinding.ListItemDishBinding +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.visible + +class SamplingAdapter(list: MutableList) : + BaseQuickAdapter(list) { + + inner class VH(var binding: ListItemDishBinding) : QuickViewHolder(binding.root) + + override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { + val inflater = LayoutInflater.from(context) + val binding = ListItemDishBinding.inflate(inflater, parent, false) + return VH(binding) + } + + override fun onBindViewHolder(holder: VH, position: Int, item: FoodRecord?) { + holder.binding.run { + val isCooking = item!!.isCooking + clBlock.setBackgroundResource(R.drawable.shape_white_fb_15_corners) + val totalWeight = item!!.totalWeight ?: 0 + tvDishName.run { + text = item!!.foodName + setTextColor( + ContextCompat.getColor( + holder.itemView.context, + if (isCooking) R.color.black else R.color.dish_green + ) + ) + } + tvDishCount.visible() + if (isCooking) { + tvDishCount.text = "制作统计:-kg" + tvShowState.text = "烹饪中" + } else { + val useWeight = if (totalWeight > 0) totalWeight else (item.foodWeight ?: 0) + tvDishCount.text = "累计统计:${useWeight / 1000.0F}kg(${item.count}次)" + tvShowState.text = "${(item.foodWeight ?: 0) / 1000.0F}kg" + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt new file mode 100644 index 0000000..813fa1b --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt @@ -0,0 +1,132 @@ +package com.shuwei.dish.match.adapter + +import android.annotation.SuppressLint +import android.widget.TextView +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.dialog.BottomDialog2 +import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.ui.DeviceSettingActivity +import com.shuwei.dish.match.ui.SubmitDishActivity +import com.shuwei.dish.match.utils.DataUtil + +object SeasoningCellTool { + private val TAG = "SeasoningCellTool" + + private val list = mutableListOf() + private lateinit var activity: BaseActivity + private var pageType: Int = 0 + + @SuppressLint("StaticFieldLeak") + private lateinit var cellAdapter: TextCellAdapter + + @SuppressLint("NotifyDataSetChanged") + fun loadData( + activity: BaseActivity, + initList: MutableList? = null, + recyclerView: RecyclerView, + tvCell: TextView, + pageType: Int = 0 + ) { + this.activity = activity + this.pageType = pageType + list.clear() + val tempList = buildData(initList, pageType) + list.addAll(tempList) + recyclerView.run { + if (adapter == null) { + layoutManager = GridLayoutManager( + activity, + 3, + GridLayoutManager.VERTICAL, + false + ) + cellAdapter = TextCellAdapter(list).apply { + onCellClick = { position -> + val currentAddress = DataUtil.getWeighAddressArray().get(position) + BottomDialog2( + weighIndex = position, + weighAddress = currentAddress, + clickName = list[position].goodsName + ).show(activity) { item -> + list[position].let { current -> + syncItem(current, item) + //3-调料 + current.materialType = 3 + current.sort = position + } + notifyItemChanged(position) + if (position == 14) { + syncItem(list[10], list[14]) + syncItem(list[11], list[14]) + syncItem(list[13], list[14]) + TextCellAdapter.loadCell(tvCell, list[14]) + notifyItemChanged(10) + notifyItemChanged(11) + notifyItemChanged(13) + notifyItemChanged(14) + } + if (activity is DeviceSettingActivity) { + //activity.list = list + activity.saveSeasoning(list[position]) + } + } + } + } + adapter = cellAdapter + } else { + cellAdapter.notifyDataSetChanged() + } + if (activity is SubmitDishActivity) { + activity.updateSeasoningList(list) + } + } + if (pageType == 1) { + tvCell.setOnClickListener { + cellAdapter.onCellClick?.invoke(14) + } + } + TextCellAdapter.loadCell(tvCell, list[list.size - 1]) + } + + private fun buildData( + initList: MutableList?, + pageType: Int = 0 + ): MutableList { + val list = mutableListOf() + repeat(15) { + list.add(SeasoningEntity().apply { this.pageType = pageType }) + } + if (initList.isNullOrEmpty().not()) { + initList.forEach { + list[it.sort] = it.apply { this.pageType = pageType } + } + var lastItem = initList.firstOrNull { it.sort == 14 } + lastItem = lastItem ?: list[list.size - 1] + list[10] = lastItem + list[11] = lastItem + list[13] = lastItem + } + return list + } + + private fun syncItem(item: SeasoningEntity, newItem: SeasoningEntity) { + item.run { + goodsId = newItem.goodsId + goodsName = newItem.goodsName + goodsOrRelationCode = newItem.goodsOrRelationCode + relateionType = newItem.relateionType + materialType = newItem.materialType + allEdible = newItem.allEdible + useWeight = newItem.useWeight + popularName = newItem.popularName + canteenId = newItem.canteenId + relateionType_dictText = newItem.relateionType_dictText + + foodId = newItem.foodId + sort = newItem.sort + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt new file mode 100644 index 0000000..1477df3 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt @@ -0,0 +1,49 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding +import com.shuwei.dish.match.entity.SeasoningEntity + +class SeasoningSearchAdapter(private var list: MutableList) : + BaseQuickAdapter(list) { + + override fun onBindViewHolder( + holder: VH, + position: Int, + item: SeasoningEntity? + ) { + holder.binding.tvGoodsInfo.run { + text = item?.goodsName + setBackgroundResource( + if (item?.isClicked == true) R.drawable.shape_green_stroke + else R.drawable.shape_white_12_corners + ) + setTextColor( + ContextCompat.getColor( + context, + if (item?.isClicked == true) R.color.dish_green + else R.color.black666 + ) + ) + } + } + + override fun onCreateViewHolder( + context: Context, + parent: ViewGroup, + viewType: Int + ): VH { + val binding = + ListItemSearchGoodsInfoBinding.inflate(LayoutInflater.from(context), parent, false) + return VH(binding) + } + + inner class VH(var binding: ListItemSearchGoodsInfoBinding) : QuickViewHolder(binding.root) + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt new file mode 100644 index 0000000..69deaca --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt @@ -0,0 +1,117 @@ +package com.shuwei.dish.match.adapter + +import android.graphics.Typeface +import android.text.SpannableStringBuilder +import android.text.style.AbsoluteSizeSpan +import android.text.style.ForegroundColorSpan +import android.text.style.LineHeightSpan +import android.text.style.StyleSpan +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.graphics.toColorInt +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.ListItemCellBinding +import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.utils.ext.appendText +import com.shuwei.dish.match.utils.ext.buildSpannableString +import com.shuwei.dish.match.utils.ext.clickWithDebounce +import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.utils.ext.invisible +import com.shuwei.dish.match.utils.ext.visible + +class TextCellAdapter(var list: MutableList) : + RecyclerView.Adapter() { + + inner class VH(var binding: ListItemCellBinding) : QuickViewHolder(binding.root) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + val inflater = LayoutInflater.from(parent.context) + val binding = ListItemCellBinding.inflate(inflater, parent, false) + return VH(binding) + } + + override fun onBindViewHolder(holder: VH, position: Int) { + val item = list[position] + holder.binding.run { + if (position == 10 || position == 11 || position == 13 || position == 14) { + root.invisible() + } else { + root.visible() + } + loadCell(tvCell, item) + //root.setBackgroundResource(R.color.dish_green) + (root.layoutParams as RecyclerView.LayoutParams).let { +// it.bottomMargin = 15.dp + it.width = 226.dp + it.height = 120.dp +// it.rightMargin = 0 +// it.leftMargin = if (position % 3 == 0) 0 else 15.dp + + it.leftMargin = 7.dp + it.rightMargin = 8.dp + it.topMargin = 7.dp + it.bottomMargin = 8.dp + } + if (item.pageType == 1) { + root.clickWithDebounce { onCellClick?.invoke(position) } + } + } + } + + override fun getItemCount() = list.size + + var onCellClick: ((positon: Int) -> Unit)? = null + + companion object { + fun loadCell(tvCell: TextView, item: SeasoningEntity) { + if (item.goodsName.isNullOrBlank()) { + tvCell.run { + text = "未配置" + setTextColor(ContextCompat.getColor(context, R.color.black999)) + } + } else { +// if (item.pageType == 1) { +// tvCell.run { +// text = item.goodsName +// setTextColor(ContextCompat.getColor(context, R.color.black)) +// } +// } else { + tvCell.text = getTextSpan(item) +// } + } + } + + fun getTextSpan(item: SeasoningEntity): SpannableStringBuilder { + val endIndex = if (item.sort == 14) 10 else 6 + var name = item.goodsName!! + if (name.length > endIndex) { + name = "${name.substring(0, endIndex)}…" + } + return buildSpannableString { + appendText( + name, + ForegroundColorSpan("#000000".toColorInt()), + StyleSpan(Typeface.BOLD), + AbsoluteSizeSpan(28, true), + LineHeightSpan { text, start, end, spanstartv, v, fm -> + fm.descent += 10.dp // 增加行间距 + } + ) + append("\n") + + val weight = item.useWeight ?: 0 + val weightColor = if (weight == 0) "#999999" else "#00BC71" + appendText( + "${weight}g", + ForegroundColorSpan(weightColor.toColorInt()), + AbsoluteSizeSpan(30, true) + ) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt b/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt new file mode 100644 index 0000000..4ed47a0 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt @@ -0,0 +1,163 @@ +package com.shuwei.dish.match.base + +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.TextView +import androidx.activity.enableEdgeToEdge +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.ActivityBaseBinding +import com.shuwei.dish.match.dialog.Loading +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.ui.HomeActivity.Companion.TAG +import com.shuwei.dish.match.utils.ActivityManager +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.put +import com.shuwei.dish.match.utils.ext.toast +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.Locale + +@Suppress("DEPRECATION") +open class BaseActivity : AppCompatActivity() { + + private lateinit var binding: ActivityBaseBinding + private var launcher: ActivityResultLauncher? = null + private var launchCallback: ((Intent?) -> Unit)? = null + + public fun launch(cls: Class<*>, launchCallback: (Intent?) -> Unit) { + this.launchCallback = launchCallback + launcher?.launch(Intent(this, cls)) + } + + public fun launch(intent: Intent, launchCallback: (Intent?) -> Unit) { + this.launchCallback = launchCallback + launcher?.launch(intent) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + ActivityManager.addActivity(this) + binding = ActivityBaseBinding.inflate(layoutInflater) + setContentView(binding.root) + statusBarDarkFont(enable = false) + launcher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + launchCallback?.invoke(result.data) + } + } + +// window.setDecorFitsSystemWindows(false) // 启用 Edge-to-Edge +// window.insetsController?.apply { +// hide(WindowInsets.Type.statusBars()) // 隐藏状态栏 +// systemBarsBehavior = +// WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE // 滑动时临时显示 +// } + binding.llTitleBar.gone() + binding.ivBack.setOnClickListener { finish() } + } + + override fun onDestroy() { + dismissLoading() + super.onDestroy() + ActivityManager.removeActivity(this) + } + + fun hideStatusBar() { + enableEdgeToEdge() +// val uiOptions = (View.SYSTEM_UI_FLAG_FULLSCREEN // 隐藏状态栏 +// or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) // 隐藏导航栏(可选) + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN + } + + fun statusBarDarkFont(enable: Boolean) { + //window.decorView.systemUiVisibility = if (enable) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else 0 + val color = ContextCompat.getColor(this, + if (enable) R.color.black else R.color.white + ) + binding.tvLeftTime.setTextColor(color) + binding.tvRightTime.setTextColor(color) + } + + fun setTitleBar( + titleBarAction: ((FrameLayout) -> Unit)? = null, + backAction: ((ImageView) -> Unit)? = null, + titleAction: ((TextView) -> Unit)? = null, + rightIconActon: ((ImageView) -> Unit)? = null + ) { + titleBarAction?.invoke(binding.llTitleBar) + backAction?.invoke(binding.ivBack) + titleAction?.invoke(binding.tvTitle) + rightIconActon?.invoke(binding.ivRightIcon) + } + + public fun setHeaderBackground(isHomePage: Boolean = false) { + binding.ivHeaderBg.setImageResource( + if (isHomePage) R.drawable.bg_home_page else R.drawable.bg_other_page + ) + } + + override fun setContentView(view: View?) { + if (view == binding.root) { + super.setContentView(view) + } else { + binding.flContainer.addView(view) + } + } + + private fun updateDateTime() { + val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE***HH:mm:ss", Locale.CHINA) + val dateTime = formatter.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai"))) + val arr = dateTime.split("***") + binding.tvRightTime.text = arr[0] + binding.tvLeftTime.text = arr[1] + } + + private val handler = Handler(Looper.getMainLooper()) + private val updateTask = object : Runnable { + override fun run() { + updateDateTime() + handler.postDelayed(this, 1000) + } + } + + override fun onResume() { + super.onResume() + hideStatusBar() + handler.post(updateTask) + } + + override fun onPause() { + super.onPause() + handler.removeCallbacks(updateTask) + } + + fun showLoading() { + Loading.show(this) + } + + fun dismissLoading() { + Loading.dismiss() + } + + fun delayDismissLoading() { + window.decorView.postDelayed({ + Loading.dismiss() + },500) + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/base/BaseApp.kt b/app/src/main/java/com/shuwei/dish/match/base/BaseApp.kt new file mode 100644 index 0000000..7647d0f --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/base/BaseApp.kt @@ -0,0 +1,36 @@ +package com.shuwei.dish.match.base + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import com.shuwei.dish.match.R +import com.shuwei.dish.match.db.DatabaseProvider + +class BaseApp : Application() { + + val database by lazy { DatabaseProvider(this).instance } + + var token: String? = null + var androidId: String? = null + + override fun onCreate() { + super.onCreate() + instance = this + } + + companion object { + @Volatile + private var sharedPref: SharedPreferences? = null + + public var instance: BaseApp? = null + fun getSharedPref(): SharedPreferences? { + if (sharedPref != null) { + return sharedPref + } + val saveName = instance?.getString(R.string.sp_save_name) + sharedPref = instance?.getSharedPreferences(saveName, Context.MODE_PRIVATE) + return sharedPref + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/base/BaseFragment.kt b/app/src/main/java/com/shuwei/dish/match/base/BaseFragment.kt new file mode 100644 index 0000000..2167f3a --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/base/BaseFragment.kt @@ -0,0 +1,9 @@ +package com.shuwei.dish.match.base + +import androidx.fragment.app.Fragment + +open class BaseFragment : Fragment() { + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/base/BaseReq.kt b/app/src/main/java/com/shuwei/dish/match/base/BaseReq.kt new file mode 100644 index 0000000..ce222f8 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/base/BaseReq.kt @@ -0,0 +1,10 @@ +package com.shuwei.dish.match.base + +class BaseReq { + var code: Int? = null + var success: Boolean? = null + var data: T? = null + var message: String? = null + var result: T? = null + var msg: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt b/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt new file mode 100644 index 0000000..1711c17 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt @@ -0,0 +1,174 @@ +package com.shuwei.dish.match.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import com.shuwei.dish.match.db.dao.AppDao +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.SeasoningEntity + + +// 步骤1:更新版本号 +@Database( + // 添加所有实体类 + entities = [ + SeasoningEntity::class, + CookFoodEntity::class, + CookFoodGoodsEntity::class, + ], + version = 6, + exportSchema = true +) +abstract class AppDatabase : RoomDatabase() { +// abstract fun seasoningDao(): SeasoningDao +// abstract fun cookFoodDao(): CookFoodDao +// abstract fun cookFoodGoodsDao(): CookFoodGoodsDao + + abstract fun appDao(): AppDao + +} + +// 步骤2:创建Migration对象 +val MIGRATION_1_2 = MigrationImpl(1, 2) { db -> + // 获取SQLite版本 + runCatching { + // 使用兼容方案 + db.beginTransaction() + db.execSQL( + """ + CREATE TABLE `dm_seasoning_new` ( + `id` INTEGER NOT NULL, + `name` TEXT, + `weight` INTEGER, + `sort` INTEGER NOT NULL, + `isDel` INTEGER NOT NULL, + `createTime` INTEGER NOT NULL, + PRIMARY KEY(`id`) + ) + """ + ) + + db.execSQL( + """ + INSERT INTO dm_seasoning_new + SELECT id, name, weight, sort, isDel, create_time + FROM dm_seasoning + """ + ) + + db.execSQL("DROP TABLE dm_seasoning") + db.execSQL("ALTER TABLE dm_seasoning_new RENAME TO dm_seasoning") + // 执行上述临时表方案 + db.setTransactionSuccessful() + }.onFailure { + it.printStackTrace() + } + db.endTransaction() +} + +val MIGRATION_3_4 = MigrationImpl(3, 4) { db -> + db.execSQL( + """ + CREATE TABLE IF NOT EXISTS `dm_cook_food` ( + `foodId` INTEGER PRIMARY KEY NOT NULL, + `foodName` TEXT, + `canteenId` INTEGER, + `cookedFoodWeight` INTEGER NOT NULL, + `isDel` INTEGER NOT NULL, + `createTime` TEXT NOT NULL + ) + """ + ) + + db.execSQL( + """ + CREATE TABLE IF NOT EXISTS `dm_cook_food_goods` ( + `goodsId` INTEGER PRIMARY KEY NOT NULL, + `goodsName` TEXT, + `foodId` INTEGER NOT NULL, + `relateionType` INTEGER NOT NULL, + `allEdible` INTEGER NOT NULL, + `useWeight` INTEGER NOT NULL, + `isDel` INTEGER NOT NULL, + `createTime` TEXT NOT NULL + ) + """ + ) +} + +val MIGRATION_4_5 = MigrationImpl(4, 5) { db -> + db.execSQL("ALTER TABLE dm_cook_food_goods ADD sort INTEGER DEFAULT (0) NOT NULL") +} +val MIGRATION_5_6 = MigrationImpl(5, 6) { db -> + runCatching { + db.beginTransaction() + db.execSQL( + """ + CREATE TABLE IF NOT EXISTS `dm_cook_food_goods_new` ( + `id` INTEGER PRIMARY KEY NOT NULL, + `goodsId` INTEGER NOT NULL, + `goodsName` TEXT, + `foodId` INTEGER NOT NULL, + `relateionType` INTEGER NOT NULL, + `allEdible` INTEGER NOT NULL, + `useWeight` INTEGER NOT NULL, + `sort` INTEGER NOT NULL DEFAULT 0, + `isDel` INTEGER NOT NULL, + `createTime` TEXT NOT NULL + ) + """ + ) + db.execSQL( + """ + INSERT INTO dm_cook_food_goods_new + SELECT NULL, goodsId, goodsName, foodId, relateionType, allEdible, useWeight, sort, isDel, createTime + FROM dm_cook_food_goods + """ + ) + + db.execSQL("DROP TABLE dm_cook_food_goods") + db.execSQL("ALTER TABLE dm_cook_food_goods_new RENAME TO dm_cook_food_goods") + // 执行上述临时表方案 + db.setTransactionSuccessful() + }.onFailure { + it.printStackTrace() + } + db.endTransaction() +} + +class DatabaseProvider(private val context: Context) { + // 单例模式确保全局唯一实例‌ + val instance: AppDatabase by lazy { + Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "app_dish_match.db" + ) + .addCallback(object : RoomDatabase.Callback() { + override fun onCreate(db: SupportSQLiteDatabase) { + // 数据库首次创建时初始化数据 + } + }) +// .addMigrations(MIGRATION_1_2) +// .addMigrations(MIGRATION_3_4) +// .addMigrations(MIGRATION_4_5) +// .addMigrations(MIGRATION_5_6) + .build() + } +} + +class MigrationImpl( + startVersion: Int, + endVersion: Int, + private val block: (db: SupportSQLiteDatabase) -> Unit +) : Migration(startVersion, endVersion) { + override fun migrate(database: SupportSQLiteDatabase) { + block(database) + } + +} + diff --git a/app/src/main/java/com/shuwei/dish/match/db/BaseDatabase.kt b/app/src/main/java/com/shuwei/dish/match/db/BaseDatabase.kt new file mode 100644 index 0000000..e4a7b1d --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/BaseDatabase.kt @@ -0,0 +1,34 @@ +package com.shuwei.dish.match.db + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Update + + +// 1. 定义基础实体接口 +interface BaseEntity { +} + +//定义回调接口 +interface OnCompleteListener { + fun onComplete() +} + +// 2. 封装通用DAO操作 +@Dao +interface BaseDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(item: T): Long + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(items: MutableList): Array + + @Update + suspend fun update(item: T) + + @Delete + suspend fun delete(item: T) + +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt b/app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt new file mode 100644 index 0000000..a42d059 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt @@ -0,0 +1,141 @@ +package com.shuwei.dish.match.db + +import com.shuwei.dish.match.db.dao.AppDao +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.SeasoningEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + + +class AppRepository(val appDao: AppDao) { + + suspend fun insertCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) { + appDao.insertCookFood(item) + } + + suspend fun updateCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) { + appDao.updateCookFood(item) + } + + suspend fun getCookFoodById(foodId: String, cookMode: Int) = withContext(Dispatchers.IO) { + appDao.getCookFoodById(foodId, cookMode) + } + + suspend fun getCookFoodList(cookMode: Int) = withContext(Dispatchers.IO) { + appDao.getCookFoodList(cookMode) + } + + suspend fun countCookFood(cookMode: Int) = withContext(Dispatchers.IO) { + appDao.countCookFood(cookMode) + } + + suspend fun deleteCookFood(foodId: String, cookMode: Int) = withContext(Dispatchers.IO) { + appDao.deleteCookFood(foodId, cookMode) + } + + suspend fun deleteCookFoodGoodsList(foodId: String) = withContext(Dispatchers.IO) { + appDao.deleteCookFoodGoodsList(foodId) + } + //-------------------------------------------------------------------------------------------- + + suspend fun insertGoods(item: CookFoodGoodsEntity) = withContext(Dispatchers.IO) { + appDao.insertGoods(item) + } + + suspend fun insertGoodsList(items: MutableList) = + withContext(Dispatchers.IO) { + appDao.insertGoodsList(items) + } + + suspend fun updateGoods(item: CookFoodGoodsEntity) = withContext(Dispatchers.IO) { + appDao.updateGoods(item) + } + + suspend fun getCookFoodGoodsList(foodId: String) = withContext(Dispatchers.IO) { + appDao.getCookFoodGoodsList(foodId) + } + + suspend fun getCookFoodGoodsListByType( + foodId: String, + materialType: Int + ) = withContext(Dispatchers.IO) { + appDao.getCookFoodGoodsListByType(foodId, materialType) + } + //-------------------------------------------------------------------------------------------- + + suspend fun getSeasoningById(id: Long) = withContext(Dispatchers.IO) { + appDao.getSeasoningById(id) + } + + suspend fun getSeasoningBySort(sort:Int) = withContext(Dispatchers.IO) { + appDao.getSeasoningBySort(sort) + } + + suspend fun getSeasoningByGoodsId(goodsId: Int) = withContext(Dispatchers.IO) { + appDao.getSeasoningByGoodsId(goodsId) + } + + suspend fun getAllStream() = withContext(Dispatchers.IO) { + appDao.getAllStream() + } + + suspend fun search(query: String) = withContext(Dispatchers.IO) { + appDao.search(query) + } + + suspend fun deleteBatch() = withContext(Dispatchers.IO) { + appDao.deleteBatch() + } + + suspend fun deleteEmptySeasoning() = withContext(Dispatchers.IO) { + appDao.deleteEmptySeasoning() + } + + suspend fun insertSeasoningList(items: MutableList) = + withContext(Dispatchers.IO) { + appDao.insertSeasoningList(items) + } + + suspend fun updateSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { + appDao.updateSeasoning(item) + } + + suspend fun insertSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { + appDao.insertSeasoning(item) + } +} +// +//class SeasoningRepository(val seasoningDao: SeasoningDao) { +// val seasoningList: Flow> = seasoningDao.getAllStream() +// +// fun addSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { +// seasoningDao.insert(item) +// } +// +// fun searchSeasoning(query: String) = withContext(Dispatchers.IO) { +// seasoningDao.search(query) +// } +//} +// +//class CookFoodRepository(val cookFoodDao: CookFoodDao) { +// fun getCookFood(foodId: String, cookMode:Int = 0) = withContext(Dispatchers.IO) { +// cookFoodDao.getCookFoodById(foodId, cookMode) +// } +// +// fun getCookFoodByList(cookMode:Int = 0) = withContext(Dispatchers.IO) { +// cookFoodDao.getCookFoodList(cookMode) +// } +// +// fun countCookFood(cookMode:Int = 0)= withContext(Dispatchers.IO) { +// cookFoodDao.countCookFood(cookMode) +// } +//} +// +//class CookFoodGoodsRepository(val cookFoodGoodsDao: CookFoodGoodsDao) { +// fun getFoodGoodsList(foodId: String) = withContext(Dispatchers.IO) { +// cookFoodGoodsDao.getCookFoodGoodsList(foodId) +// } +// +//} + diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt new file mode 100644 index 0000000..418a5ad --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt @@ -0,0 +1,90 @@ +package com.shuwei.dish.match.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.SeasoningEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface AppDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertCookFood(item: CookFoodEntity): Long + + @Update + suspend fun updateCookFood(item: CookFoodEntity) + + @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND foodId = :foodId AND cookMode = :cookMode") + suspend fun getCookFoodById(foodId: String, cookMode: Int): CookFoodEntity? + + @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND cookMode = :cookMode ORDER BY createTime DESC") + suspend fun getCookFoodList(cookMode: Int): MutableList? + + @Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND cookMode = :cookMode") + suspend fun countCookFood(cookMode: Int): Int + + @Query("DELETE FROM dm_cook_food WHERE foodId = :foodId AND cookMode = :cookMode") + suspend fun deleteCookFood(foodId: String, cookMode: Int) + + //-------------------------------------------------------------------------------------------- + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertGoods(item: CookFoodGoodsEntity): Long + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertGoodsList(items: MutableList): Array + + @Update + suspend fun updateGoods(item: CookFoodGoodsEntity) + + @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId") + suspend fun getCookFoodGoodsList(foodId: String): MutableList + + @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType") + suspend fun getCookFoodGoodsListByType( + foodId: String, + materialType: Int + ): MutableList + + @Query("DELETE FROM dm_cook_food_goods WHERE foodId = :foodId") + suspend fun deleteCookFoodGoodsList(foodId: String) + + //-------------------------------------------------------------------------------------------- + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND id = :id") + suspend fun getSeasoningById(id: Long): SeasoningEntity? + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND sort = :sort") + suspend fun getSeasoningBySort(sort: Int): SeasoningEntity? + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId = :goodsId") + suspend fun getSeasoningByGoodsId(goodsId: Int): SeasoningEntity? + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId != -1 AND goodsName != '' ORDER BY sort ASC") + fun getAllStream(): Flow> + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsName LIKE '%' || :query || '%'") + suspend fun search(query: String): MutableList + + @Query("DELETE FROM dm_seasoning WHERE isDel = 0 AND goodsId = -1 AND goodsName = ''") + suspend fun deleteEmptySeasoning(): Int + + @Query("DELETE FROM dm_seasoning WHERE isDel = 0") + suspend fun deleteBatch(): Int + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertSeasoningList(items: MutableList): Array + + @Update + suspend fun updateSeasoning(item: SeasoningEntity) + + @Insert + suspend fun insertSeasoning(item: SeasoningEntity): Long + //-------------------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt new file mode 100644 index 0000000..f09e59f --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt @@ -0,0 +1,20 @@ +//package com.shuwei.dish.match.db.dao +// +//import androidx.room.Dao +//import androidx.room.Query +//import com.shuwei.dish.match.db.BaseDao +//import com.shuwei.dish.match.entity.CookFoodEntity +// +//@Dao +//interface CookFoodDao : BaseDao { +// +// @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND foodId = :foodId AND cookMode = :cookMode") +// suspend fun getCookFoodById(foodId: String, cookMode:Int): CookFoodEntity? +// +// @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND cookMode = :cookMode") +// suspend fun getCookFoodList(cookMode:Int): MutableList? +// +// @Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND cookMode = :cookMode") +// suspend fun countCookFood(cookMode:Int): Int +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt new file mode 100644 index 0000000..cd0b298 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt @@ -0,0 +1,29 @@ +//package com.shuwei.dish.match.db.dao +// +//import androidx.room.Dao +//import androidx.room.Query +//import com.shuwei.dish.match.db.BaseDao +//import com.shuwei.dish.match.entity.CookFoodGoodsEntity +// +//@Dao +//interface CookFoodGoodsDao : BaseDao { +// +// @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId") +// suspend fun getCookFoodGoodsList(foodId: String): MutableList +// +// @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType") +// suspend fun getCookFoodGoodsListByType(foodId: String, materialType:Int): MutableList +// +//// @Query( +//// """ +//// UPDATE dm_cook_food_goods +//// SET goodsName=:entity.goodsName, foodId=:entity.foodId, relateionType=:entity.relateionType, sort=:entity.sort +//// allEdible=:entity.allEdible, useWeight=:entity.useWeight, isDel=:entity.isDel, createTime=:entity.createTime +//// WHERE isDel = 0 +//// AND foodId=:entity.foodId +//// AND goodsId=:goodsId +//// """ +//// ) +//// fun updateByGoodsId(entity: CookFoodGoodsEntity) +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt new file mode 100644 index 0000000..105e069 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt @@ -0,0 +1,25 @@ +//package com.shuwei.dish.match.db.dao +// +//import androidx.room.Dao +//import androidx.room.Query +//import com.shuwei.dish.match.db.BaseDao +//import com.shuwei.dish.match.entity.SeasoningEntity +//import kotlinx.coroutines.flow.Flow +// +//@Dao +//interface SeasoningDao : BaseDao { +// +//// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND id = :id") +//// suspend fun getSeasoningById(id: Long): SeasoningEntity? +//// +//// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 ORDER BY sort ASC") +//// fun getAllStream(): Flow> +//// +//// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND name LIKE '%' || :query || '%'") +//// suspend fun search(query: String): MutableList +//// +//// @Query("DELETE FROM dm_seasoning WHERE rowid IN " + +//// "(SELECT rowid FROM dm_seasoning LIMIT 1000)") +//// suspend fun deleteBatch(): Int +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt new file mode 100644 index 0000000..62ed156 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt @@ -0,0 +1,191 @@ +package com.shuwei.dish.match.dialog + +import android.annotation.SuppressLint +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import androidx.core.view.isEmpty +import androidx.core.widget.addTextChangedListener +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.databinding.DialogBottomSheetBinding +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.GoodsRecordBean +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.utils.KeyboardUtil +import com.shuwei.dish.match.utils.ext.addOnActionSearchListener +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toObject +import com.shuwei.dish.match.utils.ext.toast + +/** + * @param goodsType 1-调料,0-食材 + */ +class BottomDialog( + private var goodsType: Int = 1 +) { + companion object { + const val TAG = "BottomDialog" + } + + private var list = mutableListOf() + + private lateinit var binding: DialogBottomSheetBinding + private lateinit var goodsInfoSearchAdapter: GoodsInfoSearchAdapter + private var goodsName: String? = null + + private var activity: BaseActivity? = null + + fun show(activity: BaseActivity, callback: (item: CookFoodGoodsEntity) -> Unit) { + this.activity = activity + //直接使用BottomSheetDialog + var dialog: BottomSheetDialog? = null + val inflater = LayoutInflater.from(activity) + binding = DialogBottomSheetBinding.inflate(inflater) + + binding.tvSheetName.text = if (goodsType == 1) "调料检索" else "食材检索" + binding.etSheetInput.hint = if (goodsType == 1) "输入调料名称" else "输入食材名称或速记码" + goodsInfoSearchAdapter = GoodsInfoSearchAdapter(list).apply { + setOnItemClickListener { _, _, position -> + list[position].isClicked = true + notifyItemChanged(position) + Handler(Looper.getMainLooper()).postDelayed({ + callback(list[position]) + dialog?.dismiss() + }, 300) + } + } + binding.recyclerView.run { + layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false) + adapter = goodsInfoSearchAdapter + addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(rv: RecyclerView, newState: Int) { + super.onScrollStateChanged(rv, newState); + } + + override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) { + //解决RecyclerView和smartRefreshLayout滑动冲突问题 + super.onScrolled(rv, dx, dy) + val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top + binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0) + } + }) + } + dialog = BottomSheetDialog(activity, R.style.BottomSheet).apply { + setContentView(binding.root) + setCancelable(true) + behavior.skipCollapsed = false // 跳过折叠状态 +// behavior.peekHeight = 880.dp +// window?.setLayout(-1, 880.dp) + show() + setOnDismissListener { + activity.hideStatusBar() + } + } + binding.refreshLayout.setOnRefreshListener { + pageNo = 1 + getGoodsList() + } + + binding.refreshLayout.setOnLoadMoreListener { + getGoodsList() + } + binding.etSheetInput.addTextChangedListener( + onTextChanged = { text, start, before, count -> + goodsName = text.toString() + }) + binding.ivSearch.setOnClickListener { v -> + searchGoods(v) + } + binding.root.setOnClickListener { v -> + KeyboardUtil.hideKeyboard(v.context, v) + } + binding.etSheetInput.let { v -> + v.addOnActionSearchListener { + searchGoods(v) + } + } + binding.refreshLayout.setEnableRefresh(false) + } + + private fun searchGoods(v: View) { + if (goodsName.isNullOrBlank()) { + activity?.toast("请${binding.etSheetInput.hint}") + return + } + pageNo = 1 + getGoodsList() + KeyboardUtil.hideKeyboard(v.context, v) + } + + private var pageNo = 1 + private val pageSize = 120 + + /** + */ + @SuppressLint("NotifyDataSetChanged") + private fun getGoodsList() { +// val pathSegmentList = UrlConfig.QUERY_GOODS_LIST.split(delimiters = arrayOf("/"), limit = 1) + val queryParams = mutableMapOf( + "goodsType" to "$goodsType", + "canteenId" to "0", + "pageNo" to "$pageNo", + "pageSize" to "$pageSize" + ) + if (goodsName.isNullOrBlank().not()) { + queryParams.put("name", goodsName!!) + } + val sb = StringBuilder().apply { + append(UrlConfig.QUERY_GOODS_LIST) + append("?") + queryParams.forEach { (key, value) -> + append("$key=$value&") + } + } + sb.deleteCharAt(sb.length - 1) + Log.d(TAG, "getGoodsList: url = $sb") + HttpUtil.get( + url = sb.toString(), + doSuccess = { + val json = it.toJsonString() + Log.d(TAG, "getGoodsList: json=$json") + val recordBean: GoodsRecordBean? = json.toObject() + if (recordBean == null || recordBean.records.isNullOrEmpty()) { + activity?.toast(if (goodsType == 1) "暂未搜索到调料信息" else "暂未搜索到食材信息") + return@get + } + val records = recordBean.records + if (pageNo == 1) { + list.clear() + } + list.addAll(records!!) + goodsInfoSearchAdapter.notifyDataSetChanged() + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } + }, doFailure = { code, msg -> + activity?.toast(msg) + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + }) + } + +} + diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt new file mode 100644 index 0000000..466f23a --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt @@ -0,0 +1,249 @@ +package com.shuwei.dish.match.dialog + +import android.annotation.SuppressLint +import android.graphics.Typeface +import android.os.Handler +import android.os.Looper +import android.text.SpannableStringBuilder +import android.text.style.AbsoluteSizeSpan +import android.text.style.ForegroundColorSpan +import android.text.style.LineHeightSpan +import android.text.style.StyleSpan +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import androidx.core.graphics.toColorInt +import androidx.core.view.isEmpty +import androidx.core.widget.addTextChangedListener +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.aithings.Weigher +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.SeasoningSearchAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.databinding.DialogBottomSheet2Binding +import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.entity.SeasoningRecordBean +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.utils.KeyboardUtil +import com.shuwei.dish.match.utils.WeightUtil +import com.shuwei.dish.match.utils.ext.addOnActionSearchListener +import com.shuwei.dish.match.utils.ext.appendText +import com.shuwei.dish.match.utils.ext.buildSpannableString +import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toObject +import com.shuwei.dish.match.utils.ext.toast + +/** + * @param goodsType 1-调料,0-食材 + */ +class BottomDialog2( + private var weighIndex: Int, + private var weighAddress: Int, + private var clickName: String? = null +) { + companion object { + const val TAG = "BottomDialog" + } + + private val goodsType: Int = 1 + private var list = mutableListOf() + + private lateinit var binding: DialogBottomSheet2Binding + private lateinit var searchAdapter: SeasoningSearchAdapter + private var goodsName: String? = null + + private var activity: BaseActivity? = null + + fun show(activity: BaseActivity, callback: (item: SeasoningEntity) -> Unit) { + this.activity = activity + //直接使用BottomSheetDialog + var dialog: BottomSheetDialog? = null + val inflater = LayoutInflater.from(activity) + binding = DialogBottomSheet2Binding.inflate(inflater) + + //binding.tvSheetName.text = if (goodsType == 1) "调料检索" else "食材检索" + binding.etSheetInput.hint = if (goodsType == 1) "输入调料名称" else "输入食材名称或速记码" + searchAdapter = SeasoningSearchAdapter(list).apply { + setOnItemClickListener { _, _, position -> + list[position].isClicked = true + notifyItemChanged(position) + Handler(Looper.getMainLooper()).postDelayed({ + callback(list[position].also { it.useWeight = currentWeight }) + dialog?.dismiss() + }, 300) + } + } + binding.recyclerView.run { + layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false) + adapter = searchAdapter + addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(rv: RecyclerView, newState: Int) { + super.onScrollStateChanged(rv, newState); + } + + override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) { + //解决RecyclerView和smartRefreshLayout滑动冲突问题 + super.onScrolled(rv, dx, dy) + val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top + binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0) + } + }) + } + dialog = BottomSheetDialog(activity, R.style.BottomSheet).apply { + setContentView(binding.root) + setCancelable(true) + behavior.skipCollapsed = false // 跳过折叠状态 +// behavior.peekHeight = 880.dp +// window?.setLayout(-1, 880.dp) + show() + setOnDismissListener { + activity.hideStatusBar() + } + } + binding.refreshLayout.setOnRefreshListener { + pageNo = 1 + getGoodsList() + } + + binding.refreshLayout.setOnLoadMoreListener { + getGoodsList() + } + binding.etSheetInput.addTextChangedListener( + onTextChanged = { text, start, before, count -> + goodsName = text.toString() + }) + binding.ivSearch.setOnClickListener { v -> + searchGoods(v) + } + binding.root.setOnClickListener { v -> + KeyboardUtil.hideKeyboard(v.context, v) + } + binding.etSheetInput.let { v -> + v.addOnActionSearchListener { + searchGoods(v) + } + } + binding.refreshLayout.setEnableRefresh(false) + binding.tvClear.setOnClickListener { + Weigher.tareTwo(weighAddress) + } + WeightUtil.addWeightListener { address, state, weight -> + if (weighAddress == address) { + this.currentWeight = weight + binding.tvWeight.text = getTextSpan(weight) + } + } + binding.tvWeight.text = getTextSpan(0) + + if (clickName.isNullOrBlank().not()) { + binding.etSheetInput.setText(clickName!!.trim()) + pageNo = 1 + getGoodsList() + } + } + + private fun searchGoods(v: View) { + if (goodsName.isNullOrBlank()) { + activity?.toast("请${binding.etSheetInput.hint}") + return + } + pageNo = 1 + getGoodsList() + KeyboardUtil.hideKeyboard(v.context, v) + } + + private var currentWeight = 0 + private var pageNo = 1 + private val pageSize = 120 + + /** + */ + @SuppressLint("NotifyDataSetChanged") + private fun getGoodsList() { + val queryParams = mutableMapOf( + "goodsType" to "$goodsType", + "canteenId" to "0", + "pageNo" to "$pageNo", + "pageSize" to "$pageSize" + ) + if (goodsName.isNullOrBlank().not()) { + queryParams.put("name", goodsName!!) + } + val sb = StringBuilder().apply { + append(UrlConfig.QUERY_GOODS_LIST) + append("?") + queryParams.forEach { (key, value) -> + append("$key=$value&") + } + } + sb.deleteCharAt(sb.length - 1) + Log.d(TAG, "getGoodsList: url = $sb") + HttpUtil.get( + url = sb.toString(), + doSuccess = { + val json = it.toJsonString() + Log.d(TAG, "getGoodsList: json=$json") + val recordBean: SeasoningRecordBean? = json.toObject() + if (recordBean == null || recordBean.records.isNullOrEmpty()) { + activity?.toast(if (goodsType == 1) "暂未搜索到调料信息" else "暂未搜索到食材信息") + return@get + } + val records = recordBean.records + if (pageNo == 1) { + list.clear() + } + list.addAll(records!!) + searchAdapter.notifyDataSetChanged() + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } + }, doFailure = { code, msg -> + activity?.toast(msg) + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + }) + } + + fun getTextSpan(weight: Int): SpannableStringBuilder { + var topWeight = "$weight" + var bottomUnit = "克" + if (weight >= 1000) { + topWeight = "${weight / 1000F}" + bottomUnit = "千克" + } + return buildSpannableString { + appendText( + topWeight, + ForegroundColorSpan("#000000".toColorInt()), + StyleSpan(Typeface.BOLD), + AbsoluteSizeSpan(72, true), + LineHeightSpan { text, start, end, spanstartv, v, fm -> + fm.descent += 10.dp // 增加行间距 + } + ) + append("\n") + appendText( + bottomUnit, + ForegroundColorSpan("#999999".toColorInt()), + AbsoluteSizeSpan(30, true) + ) + } + } +} + + + diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt new file mode 100644 index 0000000..4700fe5 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/dialog/CommonDialog.kt @@ -0,0 +1,80 @@ +package com.shuwei.dish.match.dialog + +import android.app.Activity +import android.app.Dialog +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.View +import android.view.Window +import android.view.WindowManager +import androidx.annotation.StyleRes +import androidx.fragment.app.FragmentActivity +import com.shuwei.dish.match.databinding.DialogCommonBinding +import com.shuwei.dish.match.utils.ext.dp +import java.lang.ref.WeakReference + +open class CommonDialog( + private var activity: FragmentActivity +) : Dialog(activity) { + + private lateinit var binding: DialogCommonBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + requestWindowFeature(Window.FEATURE_NO_TITLE) + binding = DialogCommonBinding.inflate(layoutInflater) + setContentView(binding.root) + window?.run { +// decorView.setPadding(0.dp, 0, 0.dp, 0) + attributes = attributes.apply { + width = 708.dp + } + setBackgroundDrawable(ColorDrawable()) + setCancelable(false) +// setFlags( +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE +// ) + } + + binding.llContent.run { + removeAllViews() + addView(messageView) + } + addListener() + } + + private fun addListener() { + binding.btnLeft.setOnClickListener { onLeftClick?.invoke() } + binding.btnRight.setOnClickListener { onRightClick?.invoke() } + setOnDismissListener { onDismiss?.invoke() } + } + + + override fun show() { + super.show() + binding.btnLeft.text = leftText + binding.btnRight.text = rightText + } + + var messageView: View? = null + var onLeftClick: (() -> Unit)? = null + var onRightClick: (() -> Unit)? = null + + var leftText = "" + var rightText = "" + + var onDismiss: (() -> Unit)? = null + +} + +open class SafeDialog(activity: Activity, @StyleRes themeResId: Int) : + Dialog(activity, themeResId) { + private val weakContext = WeakReference(activity) + + override fun show() { + weakContext.get()?.let { + if (!it.isFinishing && !it.isDestroyed) super.show() + } + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/CustomToast.kt b/app/src/main/java/com/shuwei/dish/match/dialog/CustomToast.kt new file mode 100644 index 0000000..a46a346 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/dialog/CustomToast.kt @@ -0,0 +1,136 @@ +//package com.shuwei.dish.match.dialog +// +//import android.app.Activity +//import android.graphics.Color +//import android.os.Bundle +//import android.util.TypedValue +//import android.view.Gravity +//import android.view.WindowManager +//import android.widget.TextView +//import androidx.core.graphics.drawable.toDrawable +//import com.shuwei.dish.match.R +// +//class CustomToast( +// private val activity: Activity, +// private val message: String?, +// private val duration: Int = 2000 +//) : +// SafeDialog( +// activity = activity, +// themeResId = R.style.ToastDialogTheme, +// ) { +// +// override fun onCreate(savedInstanceState: Bundle?) { +// super.onCreate(savedInstanceState) +// setContentView(R.layout.custom_toast) +// findViewById(R.id.tvMessage)?.text = message +// setCancelable(true) +// if (message.isNullOrBlank() || +// duration <= 0 || +// activity.isDestroyed || +// activity.isFinishing +// ) { +// return +// } +// window?.run { +// setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) +// setDimAmount(0f) +// setGravity(Gravity.BOTTOM) +// val metrics = activity.resources.displayMetrics +// val marginPx = TypedValue.applyDimension( +// TypedValue.COMPLEX_UNIT_DIP, +// 120f, +// metrics +// ) +// attributes = attributes?.apply { +// y = marginPx.toInt() // 直接设置像素值 +// } +// setFlags( +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE +// ) +// } +// } +// +// override fun show() { +// runCatching { +// super.show() +// window?.decorView?.postDelayed({ +// if (message.isNullOrBlank() || activity.isDestroyed || activity.isFinishing) { +// return@postDelayed +// } +// super.dismiss() +// }, duration.toLong()) +// }.onFailure { +// it.printStackTrace() +// } +// } +// +// override fun dismiss() { +// runCatching { +// super.dismiss() +// }.onFailure { +// it.printStackTrace() +// } +// } +// +//} +// +////class CustomToast2( +//// private val activity: Activity, +//// private val message: String?, +//// private val duration: Int = 2000 +////) : +//// DialogFragment() { +//// +//// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { +//// return SafeDialog( +//// activity = activity, +//// themeResId = R.style.ToastDialogTheme, +//// ).apply { +//// setContentView(R.layout.custom_toast) +//// findViewById(R.id.tvMessage)?.text = message +//// setCancelable(true) +//// if (message.isNullOrBlank() || +//// duration <= 0 || +//// activity.isDestroyed || +//// activity.isFinishing +//// ) { +//// return@apply +//// } +//// window?.run { +//// setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) +//// setDimAmount(0f) +//// setGravity(Gravity.BOTTOM) +//// val metrics = activity.resources.displayMetrics +//// val marginPx = TypedValue.applyDimension( +//// TypedValue.COMPLEX_UNIT_DIP, +//// 120f, +//// metrics +//// ) +//// attributes = attributes?.apply { +//// y = marginPx.toInt() // 直接设置像素值 +//// } +//// setFlags( +//// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, +//// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE +//// ) +//// } +//// } +//// } +//// +//// override fun show(manager: FragmentManager, tag: String?) { +//// runCatching { +//// super.show(manager, tag) +//// Handler(Looper.getMainLooper()).postDelayed({ +//// if (message.isNullOrBlank() || activity.isDestroyed || activity.isFinishing) { +//// return@postDelayed +//// } +//// dismiss() +//// }, duration.toLong()) +//// }.onFailure { +//// it.printStackTrace() +//// } +//// } +//// +////} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/DialogTool.kt b/app/src/main/java/com/shuwei/dish/match/dialog/DialogTool.kt new file mode 100644 index 0000000..4ea6284 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/dialog/DialogTool.kt @@ -0,0 +1,26 @@ +//package com.shuwei.dish.match.dialog +// +//import android.content.Context +//import android.view.LayoutInflater +//import androidx.fragment.app.FragmentActivity +//import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding +// +//object DialogTool { +// +// fun load(activity: FragmentActivity, leftText:) { +// val layoutInflater = LayoutInflater.from(activity) +// val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater) +// CommonDialog(activity).apply { +// messageView = remindBinding.root +// leftText = "返回调整" +// rightText = "确认无误" +// onLeftClick = { dismiss() } +// onRightClick = { +// goToSubmit() +// dismiss() +// } +// onDismiss = { hideStatusBar() } +// }.show() +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt new file mode 100644 index 0000000..83fa748 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/dialog/LoadingDialog.kt @@ -0,0 +1,140 @@ +package com.shuwei.dish.match.dialog + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.WindowManager +import androidx.core.graphics.drawable.toDrawable +import com.shuwei.dish.match.R +import com.shuwei.dish.match.databinding.DialogLoadingBinding + +object Loading { + + private var dialog: LoadingDialog? = null + + fun show(context: Context) { + if (dialog?.isShowing == true) { + dialog?.dismiss() + } + + if (dialog == null) { + dialog = LoadingDialog(context) + } + dialog?.show() + } + + fun dismiss() { + dialog?.let { + if (it.isShowing) { + it.dismiss() + } + } + dialog = null + } + +} + +class LoadingDialog( + context: Context, + message: String = "加载中……", + private var onDismiss: () -> Unit = {} +) : Dialog(context, R.style.LoadingDialog) { + init { + val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context)) + binding.tvMessage.text = message + setContentView(binding.root) + setCancelable(true) + window?.run { + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + setFlags( + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + ); + } + setOnDismissListener { + onDismiss() + } + } +} + +//class LoadingDialog3( +// private val activity: Activity, +// private val message: String = "加载中……", +// private val onDismiss: () -> Unit = {} +//) : DialogFragment() { +// +// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { +// return SafeDialog( +// activity = activity, +// themeResId = R.style.ToastDialogTheme, +// ).apply { +// val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context)) +// binding.tvMessage.text = message +// setContentView(binding.root) +// setCancelable(true) +// window?.run { +// setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) +// setFlags( +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE +// ); +// } +// setOnDismissListener { +// onDismiss() +// } +// } +// } +// +// override fun dismiss() { +// super.dismiss() +// } +// +// override fun show(manager: FragmentManager, tag: String?) { +// runCatching { +// super.show(manager, tag) +// Handler(Looper.getMainLooper()).postDelayed({ +// super.dismiss() +// }, 800) +// }.onFailure { +// it.printStackTrace() +// } +// } +// +//} + +//class LoadingDialog( +// private val activity: Activity, +// private val message: String = "加载中……", +// private val onDismiss: () -> Unit = {} +//) : SafeDialog( +// activity = activity, +// themeResId = R.style.ToastDialogTheme, +//) { +// +// override fun onCreate(savedInstanceState: Bundle?) { +// super.onCreate(savedInstanceState) +// val binding = DialogLoadingBinding.inflate(LayoutInflater.from(context)) +// binding.tvMessage.text = message +// setContentView(binding.root) +// setCancelable(true) +// window?.run { +// setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) +// setFlags( +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, +// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE +// ); +// } +// setOnDismissListener { +// onDismiss() +// } +// } +// +// fun show(manager: FragmentManager, tag: String?) { +// show() +// Handler(Looper.getMainLooper()).postDelayed({ +// super.dismiss() +// }, 800) +// } +// +//} diff --git a/app/src/main/java/com/shuwei/dish/match/entity/CookFoodEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/CookFoodEntity.kt new file mode 100644 index 0000000..10faac6 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/CookFoodEntity.kt @@ -0,0 +1,26 @@ +package com.shuwei.dish.match.entity + +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import com.shuwei.dish.match.db.BaseEntity +import com.shuwei.dish.match.utils.DateTimeUtil +import java.time.LocalDateTime + +@Entity(tableName = "dm_cook_food") +data class CookFoodEntity( + @PrimaryKey + var foodId: String, + var foodName: String? = null, + var canteenId: Int? = null, + var foodWeight: Int = 0, + //0-制作模式,1-采样模式,2-品控模式 + var cookMode:Int = 0, + var isDel: Int = 0, + var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()), +) : BaseEntity { + @Ignore + var stFoodInfoConstituteList: MutableList? = null + + constructor() : this(foodId = "") // 必需的空构造 +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/entity/CookFoodGoodsEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/CookFoodGoodsEntity.kt new file mode 100644 index 0000000..8453788 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/CookFoodGoodsEntity.kt @@ -0,0 +1,76 @@ +package com.shuwei.dish.match.entity + +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import com.shuwei.dish.match.db.BaseEntity +import com.shuwei.dish.match.utils.DateTimeUtil +import java.io.Serializable +import java.time.LocalDateTime + +@Entity(tableName = "dm_cook_food_goods") +class CookFoodGoodsEntity( + @PrimaryKey(autoGenerate = true) + var id: Long = 0, + /** + * 物品id + */ + var goodsId: Int, + + /** + * 物品名称 + */ + var goodsName: String? = null, + + /** + * 菜品id + */ + var foodId: String? = "", + var goodsOrRelationCode: String? = "", + /** + * 调料数据顺序 + */ + var sort: Int = 0, + + /** + * 食材原材料:1,预制品:2 + */ + var relateionType: Int = 0, + /** + * 物品类型:1主料2辅料3调料 + */ + var materialType: Int = 0, + + /** + * 物品 是否全部可食:1-全部可食,0-部分可食,默认全部 + */ + var allEdible: Int = 1, + + /** + * 物品 用料重量 + */ + var useWeight: Int? = 0, + + var popularName: String? = "", + + var canteenId: String? = "", + + var relateionType_dictText: String? = "", + + var isDel: Int = 0, + var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()) +) : BaseEntity, Serializable { + constructor() : this(goodsId = -1) // 必需的空构造, + + @Ignore + var isSetFinished: Boolean = false + @Ignore + var isItemClicked: Boolean = false + @Ignore + var isSamplingPage: Boolean = false + @Ignore + var isNewDishType: Boolean = false + + @Ignore + var isClicked: Boolean = false +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt b/app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt new file mode 100644 index 0000000..2093179 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt @@ -0,0 +1,58 @@ +package com.shuwei.dish.match.entity + +data class HomeModeBean( + + var modeName: String = "", + var modeDesc: String = "", + var modeIconId: Int = 0, + var isLocked: Boolean = false, + + var onClick: (() -> Unit)? = null +) + +data class FoodRecordBean( + var records: MutableList? = null, + var total: Int, + var size: Int, + var current: Int, + var page: Int +) + +data class GoodsRecordBean( + var records: MutableList? = null, + var total: Int, + var size: Int, + var current: Int, + var page: Int +) + +data class SeasoningRecordBean( + var records: MutableList? = null, + var total: Int, + var size: Int, + var current: Int, + var page: Int +) + +//data class DishDetailEntity( +// var foodId: String? = null, +// var foodWeight: Double? = null, +// var foodRealWeight: Double? = null, +// var voList: List? = null +//) + +//data class DishPartEntity( +// var foodId: String? = null, +// var foodName: String? = null, +// var foodWeight: Int? = null, +// var useWeight: Int? = null, +// var useRealWeight: Int? = null, +// var goodsId: Int? = null, +// var goodsName: String? = null, +// var materialType: Int? = null, +// var isSetFinished: Boolean = false, +// var isItemClicked: Boolean = false, +// var isSamplingPage: Boolean = false +//) + + diff --git a/app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt new file mode 100644 index 0000000..0c7c247 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt @@ -0,0 +1,25 @@ +package com.shuwei.dish.match.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.shuwei.dish.match.db.BaseEntity + +//@Entity(tableName = "dm_dish") +//data class DishEntity ( +// /** +// * 菜品id +// */ +// @PrimaryKey var id: Long, +// /** +// * 菜品名称 +// */ +// var name: String? = "", +// /** +// * 菜品重量 +// */ +// var weight: Int? = 0, +// +// var sort: Int = 0, +// @ColumnInfo(name = "create_time") val createTime: Long = System.currentTimeMillis() +//): BaseEntity \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/entity/FoodRecord.kt b/app/src/main/java/com/shuwei/dish/match/entity/FoodRecord.kt new file mode 100644 index 0000000..9fd19dc --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/FoodRecord.kt @@ -0,0 +1,17 @@ +package com.shuwei.dish.match.entity + +import java.io.Serializable + +data class FoodRecord( + var foodId: String? = null, + var foodName: String? = null, + var foodWeight: Int? = null, + var totalWeight: Int? = null, + var count: Int? = null, + var isCooking: Boolean = false, + //0-制作模式,1-采样模式,2-品控模式 + var cookMode: Int = 0, + + var sort: Int = 0 +) : Serializable + diff --git a/app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt new file mode 100644 index 0000000..20c2816 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt @@ -0,0 +1,38 @@ +//package com.shuwei.dish.match.entity +// +//import androidx.room.Ignore +//import androidx.room.PrimaryKey +//import com.shuwei.dish.match.db.BaseEntity +// +//data class GoodsInfoEntity( +// @PrimaryKey(autoGenerate = true) var id: Long = 0, +// var goodsId: Int, +// var goodsName: String? = "", +// var popularName: String? = "", +// var canteenId: String? = "", +// var goodsOrRelationCode: String? = "", +// var relateionType: Int? = -1, +// var relateionType_dictText: String? = "", +// +// var sort: Int = 0, +// +// var swMaterBaseMaterial: String? = "", +// var preMaterClass: String? = "", +// var preUseWeight: Int? = 0, +// var preWeight: Int? = 0, +// var oil: String? = "", +// var sugar: String? = "", +// /** +// * isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据 +// */ +// var isDel: Int = 0, +// val createTime: Long = System.currentTimeMillis() +//) : BaseEntity { +// @Ignore +// var pageType: Int = 0 +// +// @Ignore +// var isClicked: Boolean = false +// +// constructor() : this(id = -1, goodsId = -1) // 必需的空构造 +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/entity/SeasoningEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/SeasoningEntity.kt new file mode 100644 index 0000000..1c60145 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/entity/SeasoningEntity.kt @@ -0,0 +1,69 @@ +package com.shuwei.dish.match.entity + +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import com.shuwei.dish.match.db.BaseEntity +import com.shuwei.dish.match.utils.DateTimeUtil +import java.time.LocalDateTime + +@Entity(tableName = "dm_seasoning") +data class SeasoningEntity( + @PrimaryKey(autoGenerate = true) + var id: Long = 0, + /** + * 物品id + */ + var goodsId: Int, + + /** + * 物品名称 + */ + var goodsName: String? = null, + + /** + * 菜品id + */ + var foodId: String? = "", + var goodsOrRelationCode: String? = "", + + /** + * 食材原材料:1,预制品:2 + */ + var relateionType: Int = 0, + /** + * 物品类型:1主料2辅料3调料 + */ + var materialType: Int = 3, + + /** + * 物品 是否全部可食:1-全部可食,0-部分可食,默认全部 + */ + var allEdible: Int = 1, + + /** + * 物品 用料重量 + */ + var useWeight: Int? = 0, + + var popularName: String? = "", + + var canteenId: String? = "", + + var relateionType_dictText: String? = "", + + var sort: Int = 9999, + /** + * isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据 + */ + var isDel: Int = 0, + var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()) +) : BaseEntity { + @Ignore + var pageType: Int = 0 + + @Ignore + var isClicked: Boolean = false + + constructor() : this(goodsId = -1) // 必需的空构造 +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/http/HttpClient.kt b/app/src/main/java/com/shuwei/dish/match/http/HttpClient.kt new file mode 100644 index 0000000..c46521d --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/http/HttpClient.kt @@ -0,0 +1,70 @@ +package com.shuwei.dish.match.http + +import android.annotation.SuppressLint +import android.util.Log +import okhttp3.Call +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import java.util.concurrent.TimeUnit + +import javax.net.ssl.* +import java.security.SecureRandom +import java.security.cert.X509Certificate + +class HttpClient private constructor() { + private val client: OkHttpClient by lazy { + OkHttpClient.Builder() + .apply { + connectTimeout(15, TimeUnit.SECONDS) + readTimeout(30, TimeUnit.SECONDS) + writeTimeout(15, TimeUnit.SECONDS) + sslSocketFactory(createSSLSocketFactory(), TrustAllCerts()) + hostnameVerifier { _, _ -> true } + //if (BuildConfig.Debug) { + addInterceptor(LoggingInterceptor()) + // } + } + .build() + } + + companion object { + val instance by lazy { HttpClient() } + } + + fun newCall(request: Request): Call = client.newCall(request) + + inner class LoggingInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + // 打印请求日志 + Log.d("OkHttp", "--> ${request.method} ${request.url}") + + val response = chain.proceed(request) + // 打印响应日志 + Log.d("OkHttp", "<-- ${response.code} ${response.request.url}") + + return response + } + } + + // 信任所有证书的TrustManager实现 + @SuppressLint("CustomX509TrustManager") + class TrustAllCerts : X509TrustManager { + @SuppressLint("TrustAllX509TrustManager") + override fun checkClientTrusted(chain: Array, authType: String) {} + @SuppressLint("TrustAllX509TrustManager") + override fun checkServerTrusted(chain: Array, authType: String) {} + override fun getAcceptedIssuers(): Array = arrayOf() + } + + // 创建信任所有证书的SSLSocketFactory + fun createSSLSocketFactory(): SSLSocketFactory { + val sslContext = SSLContext.getInstance("TLS").apply { + init(null, arrayOf(TrustAllCerts()), SecureRandom()) + } + return sslContext.socketFactory + } + +} diff --git a/app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt b/app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt new file mode 100644 index 0000000..381c296 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt @@ -0,0 +1,166 @@ +package com.shuwei.dish.match.http + +import android.os.Handler +import android.os.Looper +import android.util.Log +import com.google.gson.reflect.TypeToken +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.base.BaseReq +import com.shuwei.dish.match.http.HttpUtil.runMainThread +import com.shuwei.dish.match.utils.ext.put +import com.shuwei.dish.match.utils.ext.toType +import com.shuwei.dish.match.utils.ext.toast +import okhttp3.Call +import okhttp3.Callback +import okhttp3.HttpUrl +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import java.io.IOException + + +object HttpUtil { + + fun runMainThread(action: () -> Unit) { + Handler(Looper.getMainLooper()).post { + action() + } + } + + // GET请求(HTTPS) + fun get( + url: String, + doSuccess: (data: Any) -> Unit, + doFailure: (code: Int?, msg: String?) -> Unit + ) { + val token = getToken() + val request = Request.Builder() + .url(url) + .apply { + if (token.isNotBlank()) { + addHeader("X-Access-Token", token) + } + } + .get() + .build() + HttpClient.instance.newCall(request).enqueue(CallbackImpl(doSuccess, doFailure)) + } + + fun get( + isHttps: Boolean = false, + host: String = "vip.shuziweidao.com", + pathSegmentList: List, + queryParams: Map, + doSuccess: (data: Any) -> Unit, + doFailure: (code: Int?, msg: String?) -> Unit + ) { + val token = getToken() + val url = HttpUrl.Builder() + .scheme(if (isHttps) "https" else "http") + .host(host) + .apply { + pathSegmentList.forEach { addPathSegment(it) } + queryParams.forEach { (key, value) -> addQueryParameter(key, value) } + } + .build() + val request = Request.Builder() + .url(url) + .apply { + if (token.isNotBlank()) { + addHeader("Authorization", token) + } + } + .get() + .build() + HttpClient.instance.newCall(request).enqueue(CallbackImpl(doSuccess, doFailure)) + } + + // POST表单(HTTPS) +// fun postForm(url: String, params: Map, callback: Callback) { +// val formBody = FormBody.Builder().apply { +// params.forEach { (k, v) -> add(k, v) } +// }.build() +// +// Request.Builder() +// .url(url) +// .post(formBody) +// .build().let { HttpClient.instance.newCall(it).enqueue(callback) } +// } + + // POST JSON(HTTPS) + fun postJson( + url: String, + json: String, + doSuccess: (data: Any) -> Unit, + doFailure: (code: Int?, msg: String?) -> Unit + ) { + val body = json + .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) + val token = getToken() + Request.Builder() + .url(url) + .apply { + if (token.isNotBlank()) { + addHeader("X-Access-Token", token) + } + } + .post(body) + .build() + .let { HttpClient.instance.newCall(it).enqueue(CallbackImpl(doSuccess, doFailure)) } + } + +} + +class CallbackImpl( + private val doSuccess: (data: Any) -> Unit, + private val doFailure: (code: Int?, msg: String?) -> Unit +) : + Callback { + override fun onFailure(call: Call, e: IOException) { + runMainThread { + e.printStackTrace() + doFailure(-1, "服务异常,${e.message}") + } + } + + override fun onResponse(call: Call, response: Response) { + val respData = response.body?.string() + runMainThread { + Log.d("HttpUtil", respData ?: "") + runCatching { + val typeToken = object : TypeToken>() {} + val baseReq = respData?.toType>(typeToken = typeToken) + if (baseReq == null) { + doFailure(-1, "查询数据失败") + return@runCatching + } + if (baseReq.code != 200) { + doFailure(baseReq.code, baseReq.message) + if (baseReq.code == 500 || baseReq.code == 401) { + getAppToken() + } + return@runCatching + } + doSuccess(baseReq.result ?: "") + }.onFailure { + it.printStackTrace() + doFailure(-1, "解析异常") + } + } + } +} + +private fun getToken() = BaseApp.instance?.token ?:"" +//BaseApp.getSharedPref()?.getString("token", "") ?: "" + +private fun getAppToken() { + val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${BaseApp.instance?.androidId}" +// Log.d(TAG, "getToken: url = $url") + HttpUtil.get(url = url, doSuccess = { + BaseApp.getSharedPref()?.put("token" to it) + BaseApp.instance?.token = it.toString() + }) { code, msg -> + //toast(msg) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt b/app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt new file mode 100644 index 0000000..755b9be --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt @@ -0,0 +1,23 @@ +package com.shuwei.dish.match.http + +object UrlConfig { + +// const val BASE_URL = "http://vip.shuziweidao.com/shuwei-zhct" +// const val DISH_DETAIL = "$BASE_URL/scales/goodsUseList?foodId={foodId}&foodWeight={foodWeight}" +// const val DISH_LIST = "$BASE_URL/scales/getRestInfoFoods?eaId=99&type=0&foodName" + + const val BASE_URL = "http://192.168.1.207:9102" +// const val DISH_LIST = "$BASE_URL/scales/getRestInfoFoods?eaId=99&type=0&foodName" + + const val DISH_DETAIL = "$BASE_URL/food/stFoodInfoMatching/queryById" + + // "$BASE_URL/scales/generateToken?deviceId=1111111111111111111111111111" + + const val GET_TOKEN = "$BASE_URL/restaurant/equipment/stEquipment/getEquipmentToken" + const val SUBMIT_DISH = "$BASE_URL/food/stFoodInfoMatching/saveoredit" + + const val QUERY_GOODS_LIST = "$BASE_URL/food/stFoodInfoMatching/queryGoodsInfoList" + const val QUERY_FOOD_LIST = "$BASE_URL/food/stFoodInfoMatching/list" + const val SAMPLING_LIST = "$BASE_URL/food/stFoodInfoMatching/queryHistorical/goodsInfoList" + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/DeviceSettingActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/DeviceSettingActivity.kt new file mode 100644 index 0000000..4db04d0 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/DeviceSettingActivity.kt @@ -0,0 +1,265 @@ +package com.shuwei.dish.match.ui + +import android.graphics.Typeface +import android.os.Bundle +import android.text.SpannableStringBuilder +import android.text.style.AbsoluteSizeSpan +import android.text.style.ForegroundColorSpan +import android.text.style.LineHeightSpan +import android.text.style.StyleSpan +import android.util.Log +import androidx.core.graphics.toColorInt +import androidx.lifecycle.ViewModelProvider +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.SeasoningCellTool +import com.shuwei.dish.match.adapter.TextCellAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.databinding.ActivityDeviceSettingBinding +import com.shuwei.dish.match.db.AppRepository + +import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.utils.ext.appendText +import com.shuwei.dish.match.utils.ext.buildSpannableString +import com.shuwei.dish.match.utils.ext.dp +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.utils.ext.visible +import com.shuwei.dish.match.viewmodel.AppViewModel +import com.shuwei.dish.match.viewmodel.factory.AppFactory + +class DeviceSettingActivity : BaseActivity() { + + companion object { + + const val TAG = "DeviceSettingActivity" + + const val COOK_MODE = "cookMode" + + val modeList = listOf( + ModeBean("制作模式", "菜品快速制作,记录熟重"), + ModeBean("采样模式", "菜品快速制作,记录熟重"), + ModeBean("品控模式", "菜品快速制作,记录熟重") + ) + } + + data class ModeBean( + var modeName: String, + var modeDesc: String + ) + + private lateinit var binding: ActivityDeviceSettingBinding + + private lateinit var appViewModel: AppViewModel + +// public lateinit var list: MutableList + + private var cookMode: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityDeviceSettingBinding.inflate(layoutInflater) + setContentView(binding.root) + cookMode = intent.getIntExtra(COOK_MODE, 0) + initViewModel() + setHeaderBackground() + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = "设备配置" + }) + + addListener() + + loadQualitySpan(isEnable = false) + when (cookMode) { + 1 -> { + binding.rbModeCook.isChecked = true + binding.rbModeSampling.isChecked = false + loadCookSpan(isClicked = true) + loadSamplingSpan() + } + 2 -> { + binding.rbModeCook.isChecked = false + binding.rbModeSampling.isChecked = true + loadCookSpan() + loadSamplingSpan(isClicked = true) + } + else -> 0 + } + + binding.rbModeCook.setOnClickListener { + binding.rbModeCook.isChecked = true + binding.rbModeSampling.isChecked = false + loadCookSpan(isClicked = true) + loadSamplingSpan() + startActivity { } + finish() + } + binding.rbModeSampling.setOnClickListener { + binding.rbModeCook.isChecked = false + binding.rbModeSampling.isChecked = true + loadCookSpan() + loadSamplingSpan(isClicked = true) + startActivity { } + finish() + } + } + + private fun initViewModel() { + val db = BaseApp.instance!!.database + val factory = + AppFactory(AppRepository(db.appDao())) + appViewModel = + ViewModelProvider(this, factory)[AppViewModel::class.java] + } + + override fun onResume() { + super.onResume() + appViewModel.loadSeasoning { it -> + SeasoningCellTool.loadData( + activity = this, + initList = it, + recyclerView = binding.rvSeasoning, + tvCell = binding.tvRightBottomCell, + pageType = 1 + ) +// (binding.rvSeasoning.adapter as TextCellAdapter).let { adapter -> +// list = adapter.list +// } + } + } + +// private var isCookMode: Boolean = true + + private fun addListener() { +// binding.modeRadioGroup.setOnCheckedChangeListener { _, checkedId -> +// when (checkedId) { +// R.id.rbModeCook -> { +// isCookMode = true +// loadCookSpan(isClicked = true) +// loadSamplingSpan() +// loadQualitySpan(isEnable = false) +// } +// +// R.id.rbModeSampling -> { +// isCookMode = false +// loadCookSpan() +// loadSamplingSpan(isClicked = true) +// loadQualitySpan(isEnable = false) +// } +// +// R.id.rbModeQuality -> { +// loadCookSpan() +// loadSamplingSpan() +// loadQualitySpan(isEnable = false) +// } +// } +// } +// binding.btnCancel.setOnClickListener { finish() } +// binding.btnConfirm.setOnClickListener { confirmSetting() } + + } + + private fun confirmSetting() { +// runCatching { +// showLoading() +//// list.forEachIndexed { index, entity -> +//// entity.sort = index +//// } +//// val list2 = mutableListOf() +//// list2.addAll(list) +//// val array = arrayOf(10, 11, 13) +//// list.forEachIndexed { index, entity -> +//// if (entity.sort == 14 && array.contains(index)) { +//// list2.remove(entity) +//// } +//// } +// Log.d(TAG, "addListener->Click: ${list.toJsonString()}") +// +// appViewModel.addSeasoning(list) { +// dismissLoading() +// if (isCookMode) { +// startActivity { } +// } else { +// startActivity { } +// } +//// val activityStack = ActivityManager.getActivityStack() +//// activityStack.forEach { +//// if (it !is SelectDishActivity && it !is SamplingListActivity) { +//// it.finish() +//// } +//// } +// } +// }.onFailure { +// it.printStackTrace() +// toast("数据提交失败") +// } + } + + + private fun loadQualitySpan(isClicked: Boolean = false, isEnable: Boolean = true) { + binding.rbModeQuality.text = + getModeTextSpan( + topText = modeList[2].modeName, + bottomText = modeList[2].modeDesc, + topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4", + bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4" + ) + } + + private fun loadSamplingSpan(isClicked: Boolean = false, isEnable: Boolean = true) { + binding.rbModeSampling.text = + getModeTextSpan( + topText = modeList[1].modeName, + bottomText = modeList[1].modeDesc, + topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4", + bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4" + ) + } + + private fun loadCookSpan(isClicked: Boolean = false, isEnable: Boolean = true) { + binding.rbModeCook.text = + getModeTextSpan( + topText = modeList[0].modeName, + bottomText = modeList[0].modeDesc, + topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4", + bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4" + ) + } + + private fun getModeTextSpan( + topText: String, + topColor: String, + bottomText: String, + bottomColor: String + ): SpannableStringBuilder { + + + return buildSpannableString { + appendText( + topText, + ForegroundColorSpan(topColor.toColorInt()), + StyleSpan(Typeface.BOLD), + AbsoluteSizeSpan(36, true), + LineHeightSpan { text, start, end, spanstartv, v, fm -> + fm.descent += 10.dp // 增加行间距 + } + ) + append("\n") + appendText( + bottomText, + ForegroundColorSpan(bottomColor.toColorInt()), + AbsoluteSizeSpan(26, true) + ) + } + } + + fun saveSeasoning(entity: SeasoningEntity) { + appViewModel.saveSeasoning(entity){ + toast("保存成功") + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt new file mode 100644 index 0000000..ba1612f --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt @@ -0,0 +1,131 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.shuwei.dish.match.R +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.databinding.ActivitySamplingBinding +import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding +import com.shuwei.dish.match.db.AppRepository +import com.shuwei.dish.match.dialog.CommonDialog +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.ui.fragment.DishSamplingFragment +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.visible +import com.shuwei.dish.match.viewmodel.AppViewModel +import com.shuwei.dish.match.viewmodel.factory.AppFactory + +class DishSamplingActivity : BaseActivity() { + + private lateinit var binding: ActivitySamplingBinding + + var foodName: String? = null + + companion object { + public const val FOOD_NAME = "foodName" + public const val PAGE_FROM = "pageFrom" + public const val HOME = "home" + } + + private var pageFrom: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySamplingBinding.inflate(layoutInflater) + setContentView(binding.root) + pageFrom = intent.getStringExtra(PAGE_FROM) + setTitleBar() + setHeaderBackground() + initViewModel() + loadDishSamplingPage() + foodName = intent.getStringExtra(FOOD_NAME) + } + + private fun setTitleBar() { + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = "菜品采集" + }, rightIconActon = { + it.gone() + it.setImageResource(R.drawable.ic_setting) + it.setOnClickListener { + //startActivity() + } + }, backAction = { + it.visible() + it.setOnClickListener { + onBackPressed() + } + }) + } + + private var dishSamplingFragment: DishSamplingFragment? = null + + private fun loadDishSamplingPage() { + if (dishSamplingFragment == null) { + dishSamplingFragment = DishSamplingFragment() + } + dishSamplingFragment?.let { + loadFragment(it) + } + } + + private fun loadFragment(fragment: Fragment) { + supportFragmentManager.beginTransaction().apply { + replace(R.id.flContainer, fragment) + commit() + } + } + + private lateinit var appViewModel: AppViewModel + + private fun initViewModel() { + val db = BaseApp.instance!!.database + val factory = + AppFactory(AppRepository(db.appDao())) + appViewModel = + ViewModelProvider(this, factory)[AppViewModel::class.java] + } + + fun getCookFoodList(action: (MutableList?) -> Unit) { + appViewModel.getCookFoodList(cookMode = 1) { action(it) } + } + + @Suppress("DEPRECATION") + @SuppressLint("GestureBackNavigation") + @Deprecated("Deprecated in Java") + override fun onBackPressed() { + val list = dishSamplingFragment?.list + if (list.isNullOrEmpty().not()) { + //说明有新增的食材 + saveDataRemindDialog() + return + } + if (pageFrom == HOME) { + startActivity() + } + super.onBackPressed() + } + + private fun saveDataRemindDialog() { + val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater) + remindBinding.tvDialogTitle.text = "温馨提示" + remindBinding.tvDialogContent.text = "您好,当前页面存在未保存的数据,确认返回吗?" + CommonDialog(this).apply { + messageView = remindBinding.root + leftText = "取消" + rightText = "确认" + onLeftClick = { dismiss() } + onRightClick = { + dismiss() + finish() + } + onDismiss = { hideStatusBar() } + }.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt new file mode 100644 index 0000000..596f6e5 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt @@ -0,0 +1,215 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import androidx.recyclerview.widget.LinearLayoutManager +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.FoodRecordAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.databinding.ActivityFoodSearchBinding +import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding + +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.entity.FoodRecordBean +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.utils.KeyboardUtil +import com.shuwei.dish.match.utils.ext.addOnActionSearchListener +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toObject +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.utils.ext.visible +import java.io.Serializable + +class FoodSearchActivity : BaseActivity() { + + companion object { + private const val TAG = "FoodSearchActivity" + const val PAGE_TYPE = "pageType" + const val FOOD_ID = "foodId" + const val FOOD_NAME = "foodName" + } + + private lateinit var binding: ActivityFoodSearchBinding + + private var list: MutableList = mutableListOf() + private var pageType = 0 + private val recordAdapter by lazy { + FoodRecordAdapter(list).apply { + isStateViewEnable = true + setOnItemClickListener { adapter, view, position -> + if (pageType == 1) { + startActivity { + putExtra(PrepareCookActivity.FOOD_ITEM, list[position] as Serializable) + } + return@setOnItemClickListener + } + setResult(RESULT_OK, Intent().apply { + putExtra(FOOD_ID, list[position].foodId) + putExtra(FOOD_NAME, list[position].foodName) + }) + finish() + } + } + } + + private var foodName: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityFoodSearchBinding.inflate(layoutInflater) + setContentView(binding.root) + pageType = intent.getIntExtra(PAGE_TYPE, 0) + setHeaderBackground() + foodName = intent.getStringExtra(FOOD_NAME) + binding.etInputDish.setText(foodName) + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = "菜品搜索" + }, rightIconActon = { + it.gone() + it.setImageResource(R.drawable.ic_setting) + it.setOnClickListener { + //startActivity() + } + }) + binding.rvDishSearchList.run { + layoutManager = + LinearLayoutManager(this@FoodSearchActivity, LinearLayoutManager.VERTICAL, false) + adapter = recordAdapter + } + addViewListener() + } + + private fun addViewListener() { + binding.ivDishSearch.setOnClickListener { v -> + pageNo = 1 + getInputAndSearch() + KeyboardUtil.hideKeyboard(v.context, v) + } + binding.refreshLayout.setEnableRefresh(true) + binding.refreshLayout.setEnableLoadMore(false) + binding.refreshLayout.setOnRefreshListener { + pageNo = 1 + getInputAndSearch() + } + + binding.refreshLayout.setOnLoadMoreListener { + getInputAndSearch() + } + if (foodName.isNullOrBlank().not()) { + pageNo = 1 + queryListInfo(foodName!!) + } + binding.root.setOnClickListener { v -> + KeyboardUtil.hideKeyboard(v.context, v) + } + binding.etInputDish.let { v -> + v.addOnActionSearchListener { + pageNo = 1 + getInputAndSearch() + KeyboardUtil.hideKeyboard(v.context, v) + } + } + } + + private fun getInputAndSearch() { + val input = binding.etInputDish.text.toString().trim() + if (input.isBlank()) { + toast("请输入菜品名称") + binding.refreshLayout.finishRefresh() + return + } + queryListInfo(input) + } + + private var pageNo = 1 + private var pageSize = 100 + + @SuppressLint("NotifyDataSetChanged") + private fun queryListInfo(input: String) { + showLoading() + val map = mapOf( + "foodName" to input.trim(), + "pageNo" to "$pageNo", + "pageSize" to "$pageSize", + "canteenId" to "0" + ) + val sb = StringBuilder(UrlConfig.QUERY_FOOD_LIST).apply { + append("?") + map.forEach { (key, value) -> append("$key=$value&") } + } + sb.deleteCharAt(sb.length - 1) + HttpUtil.get( + url = sb.toString(), + doSuccess = { + delayDismissLoading() + finishRefresh() + binding.refreshLayout.run { + setEnableRefresh(true) + } + val json = it.toJsonString() + val recordBean: FoodRecordBean? = json.toObject() + if (recordBean == null || recordBean.records.isNullOrEmpty()) { +// toast("暂未搜索到相关菜品信息") + if (pageNo == 1) { + loadEmptyView() + } + return@get + } + val records = recordBean.records + if (pageNo == 1) { + list.clear() + } + list.addAll(records!!) + recordAdapter.notifyDataSetChanged() + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } + }, + doFailure = { code, msg -> + binding.refreshLayout.run { + setEnableRefresh(true) + } + toast(msg) + delayDismissLoading() + finishRefresh() + if (pageNo == 1) { + loadEmptyView() + } + } + ) + } + private fun finishRefresh() { + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + } + + private var emptyViewBinding: LayoutEmptyViewBinding? = null + @SuppressLint("NotifyDataSetChanged") + private fun loadEmptyView() { + list.clear() + recordAdapter.notifyDataSetChanged() + if (emptyViewBinding == null) { + emptyViewBinding = LayoutEmptyViewBinding.inflate(LayoutInflater.from(this), binding.rvDishSearchList, false) + } + emptyViewBinding!!.tvContent.text = "暂无数据" + emptyViewBinding!!.tvSubContent.text = "可以尝试换个名称重新搜索" + recordAdapter.stateView = emptyViewBinding!!.root + + binding.refreshLayout.run { + setEnableRefresh(false) + setEnableLoadMore(false) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt new file mode 100644 index 0000000..2a46941 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt @@ -0,0 +1,156 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.os.Bundle +import android.provider.Settings +import android.util.Log +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import com.aithings.Weigher +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.HomeModeAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.databinding.ActivityHomeBinding +import com.shuwei.dish.match.db.AppRepository +import com.shuwei.dish.match.entity.HomeModeBean +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.utils.WeightUtil +import com.shuwei.dish.match.utils.ext.put +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.viewmodel.AppViewModel +import com.shuwei.dish.match.viewmodel.factory.AppFactory + +class HomeActivity : BaseActivity() { + + companion object { + const val TAG = "HomeActivity" + } + + private lateinit var binding: ActivityHomeBinding + + private var connect: Boolean = false + + @SuppressLint("HardwareIds") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + statusBarDarkFont(enable = true) + initViewModel() + // 获取 ANDROID_ID + BaseApp.instance?.androidId = + Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) + + binding = ActivityHomeBinding.inflate(layoutInflater) + setContentView(binding.root) + setHeaderBackground(isHomePage = true) + + initRecyclerView() + getAppToken() + WeightUtil.connect { connect -> + this.connect = connect + } + Weigher.init() + Weigher.getWeight() + Weigher.startContinuousRead() +// binding.tvTopText.setOnClickListener { +// startActivity() +// } + +// appViewModel.deleteEmptySeasoning() +// appViewModel.deleteCookFoodAndGoods(0,"") {} +// appViewModel.getCookFoodGoodsList("") {goodsList-> +// goodsList.forEach { +// it.foodId = "1594990392084779010" +// appViewModel.updateGoods(it) +// } +// } +// appViewModel.deleteAll() + + } + + private fun initRecyclerView() { + initModeList() + binding.rvModeList.run { + layoutManager = + LinearLayoutManager(this@HomeActivity, LinearLayoutManager.VERTICAL, false) + adapter = modeAdapter + } + } + + private val modeList = mutableListOf() + private val modeAdapter by lazy { + HomeModeAdapter(modeList).apply { + addOnItemChildClickListener(R.id.layoutCard) { adapter, view, position -> + modeList[position].onClick?.invoke() + } + } + } + + private fun initModeList() { + modeList.add( + HomeModeBean( + modeName = "制作模式", + modeDesc = "菜品快速制作,记录熟重", + modeIconId = R.drawable.ic_mode_cook, + onClick = { + startActivity() + finish() + }) + ) + modeList.add( + HomeModeBean( + modeName = "采样模式", + modeDesc = "菜品快速制作,记录熟重", + modeIconId = R.drawable.ic_mode_sampling, + onClick = { + goSampling() + }) + ) + modeList.add( + HomeModeBean( + modeName = "品控模式", + modeDesc = "菜品快速制作,记录熟重", + modeIconId = R.drawable.ic_mode_quality, + isLocked = true, + onClick = { + toast("即将解锁") + }) + ) + } + + private lateinit var appViewModel: AppViewModel + + private fun initViewModel() { + val db = BaseApp.instance!!.database + val factory = AppFactory(AppRepository(db.appDao())) + appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java] + } + + private fun goSampling() { + appViewModel.countCookFood(cookMode = 1) { + if (it > 0) { + startActivity() + finish() + return@countCookFood + } + startActivity { + putExtra(DishSamplingActivity.PAGE_FROM, DishSamplingActivity.HOME) + } + finish() + } + } + + private fun getAppToken() { + val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${BaseApp.instance?.androidId}" +// Log.d(TAG, "getToken: url = $url") + HttpUtil.get(url = url, doSuccess = { + BaseApp.getSharedPref()?.put("token" to it) + BaseApp.instance?.token = it.toString() + }) { code, msg -> + toast(msg) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt new file mode 100644 index 0000000..54aeedd --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt @@ -0,0 +1,316 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Typeface +import android.os.Bundle +import android.util.Log +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import com.aithings.Weigher +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.DishPartAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.databinding.ActivityPrepareCookBinding +import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding +import com.shuwei.dish.match.dialog.BottomDialog +import com.shuwei.dish.match.dialog.CommonDialog +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.utils.SwipeCallback +import com.shuwei.dish.match.utils.WeightUtil +import com.shuwei.dish.match.utils.ext.clickWithDebounce +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toObject +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.utils.ext.visible +import java.io.Serializable + +@SuppressLint("NotifyDataSetChanged") +class PrepareCookActivity : BaseActivity() { + + companion object { + const val TAG = "CookActivity" + const val FOOD_ITEM = "foodItem" + } + + private lateinit var binding: ActivityPrepareCookBinding + + private var food: FoodRecord? = null + + private var goodsList: MutableList? = null + + @Suppress("DEPRECATION") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityPrepareCookBinding.inflate(layoutInflater) + setContentView(binding.root) + setHeaderBackground() + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = "菜品制作" + }, rightIconActon = { + it.gone() + it.setImageResource(R.drawable.ic_setting) + it.setOnClickListener { + //startActivity() + } + }, backAction = { + it.setOnClickListener { + onBackPressed() + } + }) + food = intent.extras?.getSerializable(FOOD_ITEM) as FoodRecord? + binding.tvDishName.text = food?.foodName ?: "" + addViewClickListener() + getDishDetail() + } + + private fun addViewClickListener() { + binding.ivWeightClear.setOnClickListener { + Weigher.tareTwo(2) + } + WeightUtil.addWeightListener(getWeight = { address, state, weight -> + Log.d(TAG, "addViewClickListener: address=$address,state=$state,weight=$weight") + if (address == 2) { + binding.tvDishPartWeight.text = "${weight}" + } + }) + binding.ivWeightAdd.setOnClickListener { + if (clickIndex == -1) { + toast("请选择菜品构成") + return@setOnClickListener + } + val clickItem = list[clickIndex] + clickItem.run { + if (isNewDishType && materialType != 1 && materialType != 2) { + toast("请选择主辅材类型") + return@setOnClickListener + } + } + val weight = binding.tvDishPartWeight.text.toString().toInt() + if (weight <= 0) { + toast("食材用量需要大于0") + return@setOnClickListener + } + Log.d(TAG, "addViewClickListener->useRealWeight=$weight") + clickItem.run { + useWeight = weight + isSetFinished = true + dishPartAdapter.notifyItemChanged(clickIndex) + } + } + binding.tvAddFood.clickWithDebounce { + BottomDialog(goodsType = 0).show(this) { item -> + val filterResult = list.firstOrNull { it.goodsId == item.goodsId } + if (filterResult != null) { + toast("不允许重复添加同一食材") + return@show + } + list.add(CookFoodGoodsEntity().apply { + goodsId = item.goodsId + goodsName = item.goodsName +// materialType = + isNewDishType = true + }) + onItemClick(list.size - 1) + } + } + binding.btnCook.clickWithDebounce { + val count = list.count { it.isSetFinished.not() } + if (count > 0) { + showRemindDialog() + return@clickWithDebounce + } + goToSubmit() + } + binding.rgCook.setOnCheckedChangeListener { group, checkedId -> + val tempType = + if (checkedId == R.id.rbDishTypeFirst) 1 else if (checkedId == R.id.rbDishTypeSecond) 2 else -1 + list[clickIndex].materialType = tempType + dishPartAdapter.notifyItemChanged(clickIndex) + + binding.tvDishType.text = + if (tempType == 1) "主材" else if (tempType == 2) "辅材" else "" + } + } + + private fun goToSubmit() { + getGoodsList() + if (goodsList.isNullOrEmpty()) { + toast("菜品构成信息未设置") + return + } + Log.d(TAG, "goToSubmit: goodsList:${goodsList?.toJsonString()}") + startActivity( + Intent(this, SubmitDishActivity::class.java) + .putExtra(SubmitDishActivity.GOODS_LIST, goodsList as Serializable) + .putExtra(SubmitDishActivity.FOOD_ITEM, food as Serializable) + ) + } + + private fun getGoodsList() { + if (goodsList == null) { + goodsList = mutableListOf() + } + list.forEach { + if (it.isSetFinished) { + goodsList?.add(CookFoodGoodsEntity().apply { + foodId = food!!.foodId + goodsId = it.goodsId + relateionType = it.relateionType + allEdible = it.allEdible + goodsName = it.goodsName + materialType = it.materialType + useWeight = it.useWeight ?: 0 + goodsOrRelationCode = it.goodsOrRelationCode + }) + } + } + } + + private fun showRemindDialog() { + val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater) + CommonDialog(this).apply { + messageView = remindBinding.root + leftText = "返回调整" + rightText = "确认无误" + onLeftClick = { dismiss() } + onRightClick = { + goToSubmit() + dismiss() + } + onDismiss = { hideStatusBar() } + }.show() + } + + + private fun getDishDetail() { + //649 + val url = "${UrlConfig.DISH_DETAIL}?foodId=${food?.foodId}" + HttpUtil.get( + url = url, + doSuccess = { + val json = it.toJsonString() + Log.d(TAG, "getDishDetail: json:$json") + val detail: CookFoodEntity? = json.toObject() + if (detail == null) { + toast("查询菜品信息为空") + return@get + } + loadDishDetail(detail) + }) { code, msg -> + toast(msg) + } + } + + private fun loadDishDetail(detail: CookFoodEntity) { + val voList = detail.stFoodInfoConstituteList + if (voList.isNullOrEmpty()) { + return + } + //筛选出主材和辅材 + val tempDate = voList.filter { it.materialType == 1 || it.materialType == 2 } + .apply { + forEach { it.useWeight = 0 } + } + list.clear() + list.addAll(tempDate) + firstReqSize = list.size + initRecyclerView() + } + + private var firstReqSize = 0 + private val list = mutableListOf() + private val dishPartAdapter by lazy { + DishPartAdapter(list).apply { + setOnItemClickListener { _, _, positon -> + onItemClick(positon) + } + } + } + + private fun onItemClick(positon: Int) { + this@PrepareCookActivity.clickIndex = positon + list[clickIndex].let { it -> + binding.tvDishShowName.run { + text = it.goodsName + setTypeface(typeface, Typeface.BOLD) + } + binding.tvDishType.text = + if (it.materialType == 1) "主材" else if (it.materialType == 2) "辅材" else "" + list.forEach { bean -> bean.isItemClicked = false } + it.isItemClicked = true + dishPartAdapter.notifyDataSetChanged() + + if (it.isNewDishType) { + binding.dividerDishType.visible() + binding.llDishType.visible() + } else { + binding.dividerDishType.gone() + binding.llDishType.gone() + } + } + } + + private var clickIndex = -1 + + private fun initRecyclerView() { + binding.rvDishPartList.run { + layoutManager = + LinearLayoutManager(this@PrepareCookActivity, LinearLayoutManager.VERTICAL, false) + adapter = dishPartAdapter + val itemTouchHelper = ItemTouchHelper(SwipeCallback(dishPartAdapter) { position -> + if (list[position].isNewDishType.not()) { + toast("只能删除刚添加的食材") + dishPartAdapter.notifyItemChanged(position) + return@SwipeCallback + } + dishPartAdapter.removeAt(position) + toast("已删除") + }) + itemTouchHelper.attachToRecyclerView(this) + } + } + + @Suppress("DEPRECATION") + @SuppressLint("GestureBackNavigation") + @Deprecated("Deprecated in Java") + override fun onBackPressed() { + if (list.size > firstReqSize) { + //说明有新增的食材 + saveDataRemindDialog() + return + } + val filterResult = list.firstOrNull { (it.useWeight ?: 0) > 0 } + if (filterResult != null) { + //说明有称重的数据 + saveDataRemindDialog() + return + } + super.onBackPressed() + } + + private fun saveDataRemindDialog() { + val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater) + remindBinding.tvDialogTitle.text = "温馨提示" + remindBinding.tvDialogContent.text = "您好,当前页面存在尚未保存的数据,确认返回上页吗?" + CommonDialog(this).apply { + messageView = remindBinding.root + leftText = "取消" + rightText = "确认" + onLeftClick = { dismiss() } + onRightClick = { + dismiss() + finish() + } + onDismiss = { hideStatusBar() } + }.show() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt new file mode 100644 index 0000000..69cde07 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt @@ -0,0 +1,111 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.shuwei.dish.match.R +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.databinding.ActivitySamplingBinding +import com.shuwei.dish.match.db.AppRepository +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.ui.fragment.SamplingListFragment +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.utils.ext.visible +import com.shuwei.dish.match.viewmodel.AppViewModel +import com.shuwei.dish.match.viewmodel.factory.AppFactory + +class SamplingListActivity : BaseActivity() { + + private lateinit var binding: ActivitySamplingBinding + + companion object { + const val IS_COOKING = "isCooking" + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySamplingBinding.inflate(layoutInflater) + setContentView(binding.root) + setTitleBar() + setHeaderBackground() + initViewModel() + loadSamplingListPage() + } + + private fun setTitleBar() { + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = "采样历史" + }, rightIconActon = { + it.visible() + it.setImageResource(R.drawable.ic_setting) + it.setOnClickListener { + startActivity { + putExtra(DeviceSettingActivity.COOK_MODE, 2) + } + } + }, backAction = { + it.gone() + }) + + + } + + private var samplingListFragment: SamplingListFragment? = null + + private fun loadSamplingListPage() { + if (samplingListFragment == null) { + samplingListFragment = SamplingListFragment() + } + samplingListFragment?.let { + loadFragment(it) + } + } + + private fun loadFragment(fragment: Fragment) { + supportFragmentManager.beginTransaction().apply { + replace(R.id.flContainer, fragment) + commit() + } + } + + private lateinit var appViewModel: AppViewModel + + private fun initViewModel() { + val db = BaseApp.instance!!.database + val factory = + AppFactory(AppRepository(db.appDao())) + appViewModel = + ViewModelProvider(this, factory)[AppViewModel::class.java] + } + + fun getCookFoodList(action: (MutableList?) -> Unit) { + appViewModel.getCookFoodList(cookMode = 1) { action(it) } + } + + fun deleteCookFoodAndGoods(foodId: String, action: () -> Unit) { + appViewModel.deleteCookFoodAndGoods(cookMode = 1, foodId = foodId, onFinish = action) + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + val isCooking = intent?.getBooleanExtra(IS_COOKING, false) ?: false + samplingListFragment?.refreshPage(isCooking) + } + + @Deprecated("Deprecated in Java") + @SuppressLint("GestureBackNavigation", "MissingSuperCall") + override fun onBackPressed() { + //super.onBackPressed() + //当前页面不允许返回 + //dialog = toast("不允许返回操作") + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt new file mode 100644 index 0000000..f5f00b3 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt @@ -0,0 +1,138 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import androidx.lifecycle.ViewModelProvider +import com.shuwei.dish.match.R +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.databinding.ActivitySelectDishBinding +import com.shuwei.dish.match.db.AppRepository +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.ui.fragment.DishListFragment +import com.shuwei.dish.match.utils.KeyboardUtil +import com.shuwei.dish.match.utils.ext.addOnActionSearchListener +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.utils.ext.visible +import com.shuwei.dish.match.viewmodel.AppViewModel +import com.shuwei.dish.match.viewmodel.factory.AppFactory + +class SelectDishActivity : BaseActivity() { + + companion object { + const val IS_COOKING = "isCooking" + } + + private lateinit var binding: ActivitySelectDishBinding + private var dishListFragment: DishListFragment? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySelectDishBinding.inflate(layoutInflater) + setContentView(binding.root) + setHeaderBackground() + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = "选择菜品" + }, rightIconActon = { + it.visible() + it.setImageResource(R.drawable.ic_setting) + it.setOnClickListener { + startActivity { + putExtra(DeviceSettingActivity.COOK_MODE, 1) + } + } + }, backAction = { + it.gone() + }) + initViewModel() + addViewListener() + loadFragment() + } + + private lateinit var appViewModel: AppViewModel + + var dinnerType: String = "早餐" + + private fun initViewModel() { + val db = BaseApp.instance!!.database + val factory = + AppFactory(AppRepository(db.appDao())) + appViewModel = + ViewModelProvider(this, factory)[AppViewModel::class.java] + } + + fun getCookFoodList(action: (MutableList?) -> Unit) { + appViewModel.getCookFoodList(cookMode = 0) { action(it) } + } + + fun addViewListener() { + binding.dishRadioGroup.setOnCheckedChangeListener { _, checkedId -> + dinnerType = when (checkedId) { + R.id.rbBreakfast -> "早餐" + R.id.rbLunch -> "午餐" + R.id.rbDinner -> "晚餐" + else -> "" + } + dishListFragment?.getDishList(1, false) + } +// binding.llSearchBar.setOnClickListener { +// startActivity { +// putExtra(FoodSearchActivity.PAGE_TYPE, 1) +// } +// } + binding.etInputDish.addOnActionSearchListener { + jumpSearch() + } + binding.ivDishSearch.setOnClickListener { + jumpSearch() + } + } + + private fun jumpSearch() { + val searchText = binding.etInputDish.text.toString().trim() + if (searchText.isBlank()) { + toast(binding.etInputDish.hint.toString()) + return + } + startActivity { + putExtra(FoodSearchActivity.PAGE_TYPE, 1) + putExtra(FoodSearchActivity.FOOD_NAME, searchText) + } + KeyboardUtil.hideKeyboard(this, binding.etInputDish) + } + + private fun loadFragment() { + DishListFragment.instance().let { + dishListFragment = it + supportFragmentManager.beginTransaction().apply { + replace(R.id.flSubPage, it) + commit() + } + } + } + + fun deleteCookFoodAndGoods(foodId: String, action: () -> Unit) { + appViewModel.deleteCookFoodAndGoods(cookMode = 0, foodId = foodId, onFinish = action) + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + val isCooking = intent?.getBooleanExtra(IS_COOKING, false) ?: false + dishListFragment?.getDishList(1, isCooking) + } + + @Deprecated("Deprecated in Java") + @SuppressLint("GestureBackNavigation", "MissingSuperCall") + override fun onBackPressed() { + //super.onBackPressed() + //当前页面不允许返回 +// dialog = toast("不允许返回操作") + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SubmitDishActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SubmitDishActivity.kt new file mode 100644 index 0000000..5834d4a --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/SubmitDishActivity.kt @@ -0,0 +1,393 @@ +package com.shuwei.dish.match.ui + +import android.annotation.SuppressLint +import android.os.Bundle +import android.util.Log +import android.util.SparseArray +import android.util.SparseIntArray +import androidx.core.util.forEach +import androidx.lifecycle.ViewModelProvider +import com.aithings.Weigher +import com.google.gson.reflect.TypeToken +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.SeasoningCellTool +import com.shuwei.dish.match.adapter.TextCellAdapter +import com.shuwei.dish.match.base.BaseActivity +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.databinding.ActivitySubmitDishBinding +import com.shuwei.dish.match.db.AppRepository + +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.utils.DataUtil +import com.shuwei.dish.match.utils.WeightUtil +import com.shuwei.dish.match.utils.ext.clickWithDebounce +import com.shuwei.dish.match.utils.ext.gone +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toType +import com.shuwei.dish.match.utils.ext.toast +import com.shuwei.dish.match.utils.ext.visible +import com.shuwei.dish.match.viewmodel.AppViewModel +import com.shuwei.dish.match.viewmodel.factory.AppFactory + +@SuppressLint("UseSparseArrays") +class SubmitDishActivity : BaseActivity() { + + companion object { + const val TAG = "SubmitDishActivity" + const val FOOD_ITEM = "foodItem" + const val GOODS_LIST = "goodsList" + } + + private lateinit var binding: ActivitySubmitDishBinding + + private var food: FoodRecord? = null + private var goodsList: MutableList? = null + + private var isCooking = false + + private val cookFoodEntity by lazy { + CookFoodEntity().apply { + food?.let { + foodId = it.foodId ?: "" + foodName = it.foodName + cookMode = it.cookMode + } + canteenId = 0 + } + } + + @Suppress("unchecked_cast", "DEPRECATION") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySubmitDishBinding.inflate(layoutInflater) + setContentView(binding.root) + initViewModel() + setHeaderBackground() + intent.extras?.apply { + food = getSerializable(FOOD_ITEM) as FoodRecord? + Log.d(TAG, "onCreate: cookMode=${food?.cookMode}") + isCooking = food?.isCooking ?: false + if (isCooking) { + //烹饪中,从本地查询数据 + loadSeasoningFromLocal() + } else { + //非烹饪中,从菜品主辅材配置页面带入数据 + goodsList = getSerializable(GOODS_LIST) as MutableList? + } + } + setTitleBar(titleBarAction = { + it.visible() + }, titleAction = { + it.text = food?.foodName + }, rightIconActon = { + it.gone() + it.setImageResource(R.drawable.ic_setting) + it.setOnClickListener { + //startActivity() + } + }) + addViewListener() + } + + private fun loadSeasoningFromLocal() { + appViewModel.getCookFoodGoodsList(food?.foodId!!) { it -> + goodsList = mutableListOf() + //设置主辅材数据 + goodsList?.addAll(it.filter { goods -> goods.materialType != 3 }) + + //获取调料数据并展示 + val filterList: List? = it.filter { goods -> goods.materialType == 3 } + .map { entity -> + SeasoningEntity().also { se -> + se.goodsId = entity.goodsId + se.goodsName = entity.goodsName + se.goodsOrRelationCode = entity.goodsOrRelationCode + se.relateionType = entity.relateionType + se.materialType = entity.materialType + se.allEdible = entity.allEdible + se.useWeight = entity.useWeight + se.popularName = entity.popularName + se.canteenId = entity.canteenId + se.relateionType_dictText = entity.relateionType_dictText + se.foodId = entity.foodId + se.sort = entity.sort + se.pageType = 0 + } + } + filterList?.toMutableList()?.let { cellData -> + loadTextCell(cellData) + } + } + } + + + private lateinit var seasoningList: List + private var isFirstSave = true + fun updateSeasoningList(list: MutableList) { + if (isFirstSave) { + val json = list.toJsonString() + val typeToken = object : TypeToken>() {} + seasoningList = json.toType(typeToken = typeToken) + isFirstSave = false + } + } + + private val seasoningArray by lazy { + SparseArray() + } + + + private val firstGoodsArray by lazy { + SparseArray() + } + + private fun addViewListener() { + addWeightListener() + binding.btnWeightClear.setOnClickListener { Weigher.tareTwo(1) } + + binding.btnCook.clickWithDebounce { + cook() + } + + binding.btnSubmit.clickWithDebounce { + if (cookFoodEntity.foodWeight <= 0) { + toast("未识别到菜品熟重") + return@clickWithDebounce + } + submit() + } + } + + private fun addWeightListener() { + WeightUtil.addWeightListener(getWeight = { address, state, weight -> + if (address == 1) { + cookFoodEntity.foodWeight = weight + binding.tvTotalWeight.text = "${weight / 1000f}" + } +// if (state != SensorScale.STATE_STABLE) { +// return@addWeightListener +// } + val firstWeight = firstGoodsArray.get(address, null) + if (firstWeight != weight) { + Log.d( + TAG, + "addViewListener: state=$state,address=$address,weight=$weight,firstWeight=$firstWeight" + ) + } + if (firstWeight == null) { + //未设置数据 + firstGoodsArray.put(address, weight) + return@addWeightListener + } +// val cookingWeight = +// if (isCooking && seasoningList.isNullOrEmpty().not()) +// seasoningList?.get(weightRelateArray2.get(address))?.weight ?: 0 +// else 0 +// var realUseWeight = firstWeight - weight + cookingWeight + var realUseWeight = firstWeight - weight + realUseWeight = if (realUseWeight > 0) realUseWeight else 0 +// Log.d( +// TAG, +// "addViewListener: state=$state,address=$address,总共使用重量:$realUseWeight" +// ) + seasoningArray.put(address, realUseWeight) + + refreshSeasoningWeight() + }) + } + + @SuppressLint("NotifyDataSetChanged") + private fun refreshSeasoningWeight() { + binding.rvSeasoning.adapter?.let { adapter -> + if (adapter is TextCellAdapter) { + adapter.list.let { items -> + weightRelateArray.forEach { key, value -> + items[key].useWeight = + (seasoningArray.get(value) ?: 0) + getCookingSeasoning(value) + } + } + adapter.notifyDataSetChanged() + TextCellAdapter.loadCell(binding.tvRightBottomCell, adapter.list[14]) + } + } + } + + private fun getCookingSeasoning(address: Int): Int { + if (isCooking.not()) return 0 + if (seasoningList.isEmpty()) return 0 + val weight = seasoningList[weightRelateArray2.get(address)].useWeight ?: 0 + //Log.i(TAG, "getCookingSeasoning: weight[${address}]=$weight,seasoningList=${seasoningList.toJsonString()}") + return weight + } + + private val weightRelateArray2 by lazy { + SparseIntArray().apply { + weightRelateArray.forEach { key, value -> + if (value == 3) { + put(3, 14) + } else { + put(value, key) + } + } + } + } + + private val weightRelateArray by lazy { + DataUtil.getWeighAddressArray() + } + +// private fun prepareData(acton: () -> Unit) { +// +// //只有制作和采样列表页面才能进入设置页面,提交页面无法进行设置,不存在调料数据被修改的问题 +// val seasoningJson = realSeasoningData.toJsonString() +// val typeToken = object : TypeToken>() {} +// val tempSeasoningList = seasoningJson.toType(typeToken = typeToken) +// goodsList?.addAll(tempSeasoningList) +// acton() +// +// } + + private fun cook() { + if (food == null || goodsList.isNullOrEmpty()) { + toast("未获取到菜品或构成信息") + return + } + val realSeasoningData = (binding.rvSeasoning.adapter as TextCellAdapter).list + if (realSeasoningData.isEmpty() && !isCooking) { + toast("未获取到调料信息") + return + } + showLoading() + //只有制作和采样列表页面才能进入设置页面,提交页面无法进行设置,不存在调料数据被修改的问题 + val seasoningJson = realSeasoningData.toJsonString() + val typeToken = object : TypeToken>() {} + val tempSeasoningList = seasoningJson.toType(typeToken = typeToken) + goodsList?.addAll(tempSeasoningList) + + val isSamplingData = food!!.cookMode == 1 + if (isSamplingData) { + //生成临时foodId,解决采样模式没有foodId,无法关联数据的问题 + val tempFoodId = cookFoodEntity.foodId.ifBlank { "${System.currentTimeMillis()}" } + cookFoodEntity.foodId = tempFoodId + } + //全部设置同一foodId + goodsList?.forEach { + it.foodId = cookFoodEntity.foodId + } + //保存菜品-保存主辅料调料 + appViewModel.saveCookFoodAndGoods( + cookMode = food!!.cookMode, + entity = cookFoodEntity, + list = goodsList, + onFinish = { + runOnUiThread { + dismissLoading() + jumpPage(isSamplingData, true) + } + }) + } + + private fun submit() { + if (food == null || goodsList.isNullOrEmpty()) { + toast("未获取到菜品或构成信息") + return + } + val realSeasoningData = (binding.rvSeasoning.adapter as TextCellAdapter).list + if (realSeasoningData.isEmpty() && !isCooking) { + toast("未获取到调料信息") + return + } + showLoading() + //只有制作和采样列表页面才能进入设置页面,提交页面无法进行设置,不存在调料数据被修改的问题 + val seasoningJson = realSeasoningData.toJsonString() + val typeToken = object : TypeToken>() {} + val tempSeasoningList = seasoningJson.toType(typeToken = typeToken) + tempSeasoningList.forEach { entity -> + val realWeight = entity.useWeight ?: 0 + val filterData = goodsList?.firstOrNull { it.goodsId == entity.goodsId } + //确保重量大于0,且无重复数据 + if (realWeight > 0 && filterData == null) { + goodsList?.add(entity) + } + } + + cookFoodEntity.stFoodInfoConstituteList = this@SubmitDishActivity.goodsList + val isSamplingData = food!!.cookMode == 1 + if (isSamplingData) { + //采样模式没有foodId + cookFoodEntity.foodId = "" + goodsList?.forEach { it.foodId = "" } + } + val json = cookFoodEntity.toJsonString() + Log.d(TAG, "submit: json=$json") + HttpUtil.postJson( + url = UrlConfig.SUBMIT_DISH, + json = json, + doSuccess = { + Log.d(TAG, "submit: postJson=$it") + //删除本地数据 + appViewModel.deleteCookFoodAndGoods( + cookMode = food!!.cookMode, + foodId = food!!.foodId!! + ) { + dismissLoading() + jumpPage(isSamplingData, false) + } + }, doFailure = { code, msg -> + Log.d(TAG, "submit: postJson:code=$code,msg=$msg") + toast(msg) + dismissLoading() + }) + } + + private fun jumpPage(isSampling: Boolean, isCooking: Boolean) { + if (isSampling) { + startActivity { + putExtra(SelectDishActivity.IS_COOKING, isCooking) + } + return + } + startActivity { + putExtra(SelectDishActivity.IS_COOKING, isCooking) + } + } + + private lateinit var appViewModel: AppViewModel + + private fun initViewModel() { + val db = BaseApp.instance!!.database + val factory = + AppFactory(AppRepository(db.appDao())) + appViewModel = + ViewModelProvider(this, factory)[AppViewModel::class.java] + } + + override fun onResume() { + super.onResume() + binding.tvRightBottomCell.visible() + if (isCooking.not()) { + //烹饪中使用本地数据,未烹饪则使用默认的调料配置数据 + appViewModel.loadSeasoning { + Log.d(TAG, "onResume: loadSeasoning:${it.toJsonString()}") + loadTextCell(it) + } + } + } + + private fun loadTextCell(list: MutableList) { + SeasoningCellTool.loadData( + activity = this, + initList = list, + recyclerView = binding.rvSeasoning, + tvCell = binding.tvRightBottomCell + ) + } +} + + diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt new file mode 100644 index 0000000..ee60de9 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt @@ -0,0 +1,243 @@ +package com.shuwei.dish.match.ui.fragment + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import com.shuwei.dish.match.adapter.DishShowAdapter +import com.shuwei.dish.match.databinding.FragmentDishListBinding +import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding + +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.entity.FoodRecordBean +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.ui.PrepareCookActivity +import com.shuwei.dish.match.ui.SelectDishActivity +import com.shuwei.dish.match.ui.SubmitDishActivity +import com.shuwei.dish.match.utils.SwipeCallback +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toObject +import com.shuwei.dish.match.utils.ext.toast +import java.io.Serializable + +class DishListFragment : Fragment() { + + companion object { + private const val TAG = "DishListFragment" + + // const val DINNER_TYPE = "dinnerType" + public fun instance(): DishListFragment { + return DishListFragment().apply { +// arguments = Bundle().apply { +// putInt(DINNER_TYPE, type) +// } + } + } + } + + // private var dinnerType = "" + private var pageNo = 1 + private val pageSize = 30 + + private lateinit var activity: SelectDishActivity + + private var list: MutableList = mutableListOf() + private val dishAdapter by lazy { + DishShowAdapter(list = list).apply { + isStateViewEnable = true + setOnItemClickListener { adapter, view, position -> + val item = list[position] + if (item.isCooking) { + //烹饪中,跳到称熟重页面 + context.startActivity { + putExtra( + SubmitDishActivity.FOOD_ITEM, + item as Serializable + ) + } + return@setOnItemClickListener + } + context.startActivity { + putExtra( + SubmitDishActivity.FOOD_ITEM, + item as Serializable + ) + } + } + + } + } + + private lateinit var binding: FragmentDishListBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentDishListBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + activity = requireActivity() as SelectDishActivity + //toast("type=$type") + binding.rvDishList.run { + layoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + adapter = dishAdapter + val itemTouchHelper = ItemTouchHelper(SwipeCallback(dishAdapter) { position -> + if (list[position].isCooking.not()) { + activity.toast("只能删除烹饪中的菜品") + dishAdapter.notifyItemChanged(position) + return@SwipeCallback + } + activity.deleteCookFoodAndGoods(foodId = list[position].foodId ?: "") { + dishAdapter.removeAt(position) + activity.toast("已删除") + } + }) + itemTouchHelper.attachToRecyclerView(this) + } + addViewListener() + + pageNo = 1 + activity.showLoading() + getDishList() + } + + private fun addViewListener() { + binding.refreshLayout.run { + setEnableRefresh(true) + setEnableLoadMore(false) + setOnRefreshListener { + pageNo = 1 + getDishList() + } + setOnLoadMoreListener { + getDishList() + } + } + } + + fun getDishList(pageNo: Int, isCooking: Boolean) { + this.pageNo = pageNo + getDishList() + } + + @SuppressLint("NotifyDataSetChanged") + @Suppress("unchecked_cast") + fun getDishList() { + activity.showLoading() + val map = mapOf( +// "foodName" to input, + "dinnerType" to activity.dinnerType, + "pageNo" to "$pageNo", + "pageSize" to "$pageSize", + "canteenId" to "0" + ) + val sb = StringBuilder(UrlConfig.QUERY_FOOD_LIST).apply { + append("?") + map.forEach { (key, value) -> append("$key=$value&") } + } + sb.deleteCharAt(sb.length - 1) + HttpUtil.get( + url = sb.toString(), + doSuccess = { + binding.refreshLayout.run { + setEnableRefresh(true) + } + activity.delayDismissLoading() + finishRefresh() + val json = it.toJsonString() + val recordBean: FoodRecordBean? = json.toObject() + if (recordBean == null || recordBean.records.isNullOrEmpty()) { + //toast("暂未搜索到相关菜品信息") + if (pageNo == 1) { + loadEmptyView() + } + return@get + } + val records = recordBean.records + if (pageNo == 1) { + list.clear() + } + list.addAll(records!!) + dishAdapter.notifyDataSetChanged() + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } + refreshLocalData(list) + }, doFailure = { code, msg -> + binding.refreshLayout.run { + setEnableRefresh(true) + } + toast(msg) + finishRefresh() + if (pageNo == 1) { + loadEmptyView() + } + activity.delayDismissLoading() + }) + } + + private var emptyViewBinding: LayoutEmptyViewBinding? = null + + @SuppressLint("NotifyDataSetChanged") + private fun loadEmptyView() { + list.clear() + dishAdapter.notifyDataSetChanged() + if (emptyViewBinding == null) { + emptyViewBinding = LayoutEmptyViewBinding.inflate( + LayoutInflater.from(requireContext()), + binding.rvDishList, + false + ) + } + emptyViewBinding!!.tvContent.text = "暂无数据" + emptyViewBinding!!.tvSubContent.text = "获取菜品数据失败,请检查网络设置或稍后重试" + dishAdapter.stateView = emptyViewBinding!!.root + //dishAdapter.setStateViewLayout(requireContext(), R.layout.layout_empty_view) + } + + private fun finishRefresh() { + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + } + + @SuppressLint("NotifyDataSetChanged") + private fun refreshLocalData(list: MutableList) { + activity.getCookFoodList { cookFoodEntities -> + if (cookFoodEntities.isNullOrEmpty()) { + return@getCookFoodList + } + val tempList = mutableListOf() + + cookFoodEntities.forEachIndexed { index, entity -> + val food = list.firstOrNull { it.foodId == entity.foodId } + if (food != null) { + food.isCooking = true + food.sort = index + tempList.add(food) + } + } + list.removeAll(tempList) + list.addAll(0, tempList) + dishAdapter.notifyDataSetChanged() + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishSamplingFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishSamplingFragment.kt new file mode 100644 index 0000000..a19fa65 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishSamplingFragment.kt @@ -0,0 +1,187 @@ +package com.shuwei.dish.match.ui.fragment + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import com.aithings.Weigher +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.DishPartAdapter +import com.shuwei.dish.match.databinding.FragmentDishSamplingBinding +import com.shuwei.dish.match.dialog.BottomDialog +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.ui.FoodSearchActivity +import com.shuwei.dish.match.ui.DishSamplingActivity +import com.shuwei.dish.match.ui.SubmitDishActivity +import com.shuwei.dish.match.utils.KeyboardUtil +import com.shuwei.dish.match.utils.WeightUtil +import com.shuwei.dish.match.utils.ext.addOnActionSearchListener +import com.shuwei.dish.match.utils.ext.clickWithDebounce +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toast +import java.io.Serializable + +class DishSamplingFragment : Fragment() { + + private val TAG = "DishSamplingFragment" + + private lateinit var activity: DishSamplingActivity + private lateinit var binding: FragmentDishSamplingBinding + + val list: MutableList = mutableListOf() + + private var tempDishEntity: CookFoodGoodsEntity? = null + + private var dishMaterialType = 1 + + private val dishAdapter by lazy { + DishPartAdapter(list).apply { + addOnItemChildClickListener(R.id.ivOperateIcon) { _, _, positon -> + //删除操作 + removeAt(positon) + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentDishSamplingBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + activity = requireActivity() as DishSamplingActivity + addViewClickListener() + foodName = activity.foodName + binding.etInputDish.setText(foodName) + } + + @SuppressLint("NotifyDataSetChanged") + private fun addViewClickListener() { + binding.rvDishPartList.run { + layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) + adapter = dishAdapter + } + binding.tvInputDishType.run { + clickWithDebounce { + BottomDialog(goodsType = 0).show(activity) { item -> + val filterResult = list.firstOrNull { it.goodsName == item.goodsName } + if (filterResult != null) { + toast("不允许重复添加同一食材") + return@show + } + text = item.goodsName + tempDishEntity = CookFoodGoodsEntity().apply { + goodsId = item.goodsId + goodsName = item.goodsName + + relateionType = item.relateionType?:0 + goodsOrRelationCode = item.goodsOrRelationCode + + // TODO: 待定 + //allEdible = item.allEdible + } + } + } + } + + binding.ivWeightClear.setOnClickListener { + Weigher.tareTwo(2) + } + WeightUtil.addWeightListener(getWeight = { address, state, weight -> + Log.d(TAG, "addViewClickListener: address=$address,state=$state,weight=$weight") + if (address == 2) { + tempDishEntity?.useWeight = weight + binding.tvDishPartWeight.text = "${weight}" + } + }) + binding.ivWeightAdd.setOnClickListener { + if (tempDishEntity == null) { + activity.toast("请输入食材名称") + return@setOnClickListener + } + if (binding.rgSampling.checkedRadioButtonId == -1) { + activity.toast("请选择主辅材类型") + return@setOnClickListener + } + if (tempDishEntity!!.useWeight == null) { + activity.toast("请将食材放在称上称重") + return@setOnClickListener + } + tempDishEntity!!.apply { + isSamplingPage = true + materialType = dishMaterialType + } + val result = list.firstOrNull { it.goodsId == tempDishEntity!!.goodsId } + if (result != null) { + activity.toast("${result.goodsName}食材已添加,请勿重复操作") + return@setOnClickListener + } + list.add(tempDishEntity!!) + dishAdapter.notifyDataSetChanged() + tempDishEntity = null + binding.tvInputDishType.text = "" + binding.rgSampling.clearCheck() + } + binding.rgSampling.setOnCheckedChangeListener { group, checkedId -> + dishMaterialType = if (checkedId == R.id.rbDishTypeFirst) 1 else 2 + } + binding.ivDishSearch.setOnClickListener { v -> + jumpSearch(v) + } + binding.btnCook.clickWithDebounce { + val showFoodName = binding.etInputDish.text.toString().trim() + if (showFoodName.isBlank()) { + toast("菜品名称为空") + return@clickWithDebounce + } + if (list.isEmpty()) { + toast("菜品构成信息未设置") + return@clickWithDebounce + } + val food = FoodRecord() + food.foodId = foodId + food.foodName = showFoodName + food.cookMode = 1 + Log.d(TAG, "goToSubmit: goodsList:${list.toJsonString()}") + activity.startActivity { + putExtra(SubmitDishActivity.GOODS_LIST, list as Serializable) + putExtra(SubmitDishActivity.FOOD_ITEM, food as Serializable) + } + } + binding.etInputDish.let { v -> + v.addOnActionSearchListener { + jumpSearch(v) + } + } + } + + private fun jumpSearch(v: View) { + activity.launch(Intent(activity, FoodSearchActivity::class.java).apply { + putExtra(FoodSearchActivity.FOOD_NAME, binding.etInputDish.text.toString().trim() ) + }) { intent -> + intent?.let { + foodId = it.getStringExtra(FoodSearchActivity.FOOD_ID) + foodName = it.getStringExtra(FoodSearchActivity.FOOD_NAME) + binding.etInputDish.run { + setText(foodName) + setSelection(text.length) + } + } + } + KeyboardUtil.hideKeyboard(v.context, v) + } + + + private var foodId: String? = null + private var foodName: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/SamplingListFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/SamplingListFragment.kt new file mode 100644 index 0000000..69d22bf --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/SamplingListFragment.kt @@ -0,0 +1,254 @@ +package com.shuwei.dish.match.ui.fragment + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import com.shuwei.dish.match.R +import com.shuwei.dish.match.adapter.SamplingAdapter +import com.shuwei.dish.match.databinding.FragmentSamplingListBinding +import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding + +import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.entity.FoodRecordBean +import com.shuwei.dish.match.http.HttpUtil +import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.ui.DishSamplingActivity +import com.shuwei.dish.match.ui.SamplingListActivity +import com.shuwei.dish.match.ui.SubmitDishActivity +import com.shuwei.dish.match.utils.DateTimeUtil +import com.shuwei.dish.match.utils.SwipeCallback +import com.shuwei.dish.match.utils.ext.startActivity +import com.shuwei.dish.match.utils.ext.toJsonString +import com.shuwei.dish.match.utils.ext.toObject +import com.shuwei.dish.match.utils.ext.toast +import java.io.Serializable + +class SamplingListFragment : Fragment() { + + companion object { + private const val TAG = "SamplingListFragment" + } + + private lateinit var activity: SamplingListActivity + private lateinit var binding: FragmentSamplingListBinding + + private var list: MutableList = mutableListOf() + private val dishAdapter by lazy { + SamplingAdapter(list = list).apply { + isStateViewEnable = true + setOnItemClickListener { adapter, view, position -> + val item = list[position] + if (item.isCooking) { + //烹饪中,跳到称熟重页面 + context.startActivity { + putExtra( + SubmitDishActivity.FOOD_ITEM, + item as Serializable + ) + } + return@setOnItemClickListener + } + context.startActivity { + putExtra(DishSamplingActivity.FOOD_NAME, item.foodName) + } + } + } + } + + @SuppressLint("NotifyDataSetChanged") + private fun initRecyclerView() { + binding.rvSamplingList.run { + layoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + adapter = dishAdapter + val itemTouchHelper = ItemTouchHelper(SwipeCallback(dishAdapter) { position -> + if (list[position].isCooking.not()) { + activity.toast("只能删除烹饪中的菜品") + dishAdapter.notifyItemChanged(position) + return@SwipeCallback + } + activity.deleteCookFoodAndGoods(foodId = list[position].foodId?:"") { + if (list.size > 1) { + dishAdapter.removeAt(position) + } else { + loadEmptyView() + } + activity.toast("已删除") + } + }) + itemTouchHelper.attachToRecyclerView(this) + } + } + + private fun addViewListener() { + binding.btnAddSampling.setOnClickListener { + activity.startActivity() + } + binding.refreshLayout.setOnRefreshListener { + pageNo = 1 + getSamplingList() + } + binding.refreshLayout.setOnLoadMoreListener { + getSamplingList() + } + binding.dishRadioGroup.setOnCheckedChangeListener { group, checkedId -> + if (checkedId == R.id.rbCooking) { + refreshLocalData() + } else { + pageNo = 1 + getSamplingList() + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentSamplingListBinding.inflate(inflater, container, false) + return binding.root + } + + @SuppressLint("NotifyDataSetChanged") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + activity = requireActivity() as SamplingListActivity + initRecyclerView() + addViewListener() + refreshLocalData() + } + + var pageNo = 1 + private val pageSize = 30 + + @SuppressLint("NotifyDataSetChanged") + fun getSamplingList() { + activity.showLoading() + val map = mapOf( +// "foodName" to input, + "pageNo" to "$pageNo", + "pageSize" to "$pageSize", + "canteenId" to "0" + ) + val sb = StringBuilder(UrlConfig.SAMPLING_LIST).apply { + append("?") + map.forEach { (key, value) -> append("$key=$value&") } + } + sb.deleteCharAt(sb.length - 1) + HttpUtil.get( + url = sb.toString(), + doSuccess = { + binding.refreshLayout.run { + setEnableRefresh(true) + } + activity.delayDismissLoading() + finishRefresh() + val json = it.toJsonString() + val recordBean: FoodRecordBean? = json.toObject() + if (recordBean == null || recordBean.records.isNullOrEmpty()) { + //toast("暂未搜索到相关菜品信息") + if (pageNo == 1) { + loadEmptyView() + } + return@get + } + val records = recordBean.records + if (pageNo == 1) { + list.clear() + } + list.addAll(records!!) + dishAdapter.notifyDataSetChanged() + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + binding.refreshLayout.setEnableRefresh(true) + if (isLoadMoreEnable) { + pageNo++ + } + }, doFailure = { code, msg -> + binding.refreshLayout.run { + setEnableRefresh(true) + } + toast(msg) + finishRefresh() + binding.refreshLayout.setEnableRefresh(true) + activity.delayDismissLoading() + if (pageNo == 1) { + loadEmptyView() + } + }) + } + + private fun finishRefresh() { + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + } + + private var emptyViewBinding:LayoutEmptyViewBinding?=null + + @SuppressLint("NotifyDataSetChanged") + private fun loadEmptyView() { + list.clear() + dishAdapter.notifyDataSetChanged() + if (emptyViewBinding == null) { + emptyViewBinding = LayoutEmptyViewBinding.inflate(LayoutInflater.from(requireContext()), binding.rvSamplingList, false) + } + emptyViewBinding!!.tvContent.text = "暂无数据" + emptyViewBinding!!.tvSubContent.text = "今日暂无采样数据,点击下方按钮新增采样" + dishAdapter.stateView = emptyViewBinding!!.root + //dishAdapter.setStateViewLayout(requireContext(), R.layout.layout_empty_view) + + binding.refreshLayout.run { + setEnableRefresh(false) + setEnableLoadMore(false) + } + } + + fun refreshPage(isCooking: Boolean) { + binding.dishRadioGroup.check( + if (isCooking) R.id.rbCooking else R.id.rbFinished + ) + } + + @SuppressLint("NotifyDataSetChanged") + fun refreshLocalData() { + activity.showLoading() + //查询本地数据 + activity.getCookFoodList { cookFoodEntities -> + activity.delayDismissLoading() + if (cookFoodEntities.isNullOrEmpty()) { + loadEmptyView() + return@getCookFoodList + } + val tempList = mutableListOf() + + cookFoodEntities + .sortedByDescending { DateTimeUtil.convert(dateStr = it.createTime) } + .forEach { entity -> + tempList.add(FoodRecord().apply { + foodId = entity.foodId + foodName = entity.foodName + cookMode = 1 + isCooking = true + }) + } + list.clear() + list.addAll(tempList) + dishAdapter.notifyDataSetChanged() + binding.refreshLayout.run { + setEnableRefresh(false) + setEnableLoadMore(false) + } + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/ActivityManager.kt b/app/src/main/java/com/shuwei/dish/match/utils/ActivityManager.kt new file mode 100644 index 0000000..5088a50 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/ActivityManager.kt @@ -0,0 +1,52 @@ +package com.shuwei.dish.match.utils + +import android.app.Activity +import java.util.Stack + +object ActivityManager { + + private val activityStack = Stack() + + // 添加Activity到栈 + fun addActivity(activity: Activity) { + activityStack.add(activity) + } + + // 移除指定Activity + fun removeActivity(activity: Activity) { + activityStack.remove(activity) + } + + // 获取当前Activity + fun currentActivity(): Activity? { + return if (activityStack.isEmpty()) null else activityStack.lastElement() + } + + // 结束指定Activity + fun finishActivity(activity: Activity) { + if (!activity.isFinishing) { + activity.finish() + } + } + + // 结束所有Activity + fun finishAllActivity() { + activityStack.forEach { + if (!it.isFinishing) { + it.finish() + } + } + activityStack.clear() + } + + // 退出应用程序 + fun exitApp() { + finishAllActivity() + android.os.Process.killProcess(android.os.Process.myPid()) + } + + fun getActivityStack(): Stack{ + return activityStack + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/DataUtil.kt b/app/src/main/java/com/shuwei/dish/match/utils/DataUtil.kt new file mode 100644 index 0000000..4ef9bb1 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/DataUtil.kt @@ -0,0 +1,28 @@ +package com.shuwei.dish.match.utils + +import android.util.SparseIntArray + +object DataUtil { + + fun getWeighAddressArray(): SparseIntArray { + return SparseIntArray().apply { + put(0, 12) + put(1, 13) + put(2, 14) + put(3, 9) + put(4, 10) + put(5, 11) + put(6, 6) + put(7, 7) + put(8, 8) + put(9, 5) + put(12, 4) + put(10, 3) + put(11, 3) + put(13, 3) + put(14, 3) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/DateTimeUtil.kt b/app/src/main/java/com/shuwei/dish/match/utils/DateTimeUtil.kt new file mode 100644 index 0000000..12983d7 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/DateTimeUtil.kt @@ -0,0 +1,32 @@ +package com.shuwei.dish.match.utils + +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.util.Date + + +object DateTimeUtil { + + const val YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss" + + fun formatDateTime(dateTime: LocalDateTime, pattern: String = YYYY_MM_DD_HH_MM_SS): String { + val formatter = DateTimeFormatter.ofPattern(pattern) + return dateTime.format(formatter) + } + + fun convert(dateStr: String, pattern: String = YYYY_MM_DD_HH_MM_SS): Date { + val formatter = DateTimeFormatter.ofPattern(pattern) + val ldt = LocalDateTime.parse(dateStr, formatter) + val zdt = ldt.atZone(ZoneId.systemDefault()) + return Date.from(zdt.toInstant()) + } + + fun main() { + val now = LocalDateTime.now() + println("默认格式: ${formatDateTime(now)}") + println("自定义格式: ${formatDateTime(now, "yyyy年MM月dd日 HH时mm分ss秒")}") + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/DecimalAdapter.kt b/app/src/main/java/com/shuwei/dish/match/utils/DecimalAdapter.kt new file mode 100644 index 0000000..7395bda --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/DecimalAdapter.kt @@ -0,0 +1,26 @@ +package com.shuwei.dish.match.utils + +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.JsonWriter + +class DecimalAdapter : TypeAdapter() { + override fun write(out: JsonWriter, value: Number) { +// out.value(value.toString()) // 强制以字符串形式写入 + out.value("$value") // 强制以字符串形式写入 + } + + override fun read(reader: JsonReader): Number { + return when (reader.peek()) { + JsonToken.NUMBER -> reader.nextDouble() + JsonToken.STRING -> reader.nextString().toDouble() + else -> 0.0 + } + } +} + +// 使用方式 +//val gson = GsonBuilder() +// .registerTypeAdapter(Double::class.java, DecimalAdapter()) +// .create() diff --git a/app/src/main/java/com/shuwei/dish/match/utils/KeyboardUtil.kt b/app/src/main/java/com/shuwei/dish/match/utils/KeyboardUtil.kt new file mode 100644 index 0000000..e978de1 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/KeyboardUtil.kt @@ -0,0 +1,17 @@ +package com.shuwei.dish.match.utils + +import android.content.Context +import android.view.View +import android.view.inputmethod.InputMethodManager + + +object KeyboardUtil { + + fun hideKeyboard(context: Context, view: View) { + val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(view.windowToken, 0) + view.clearFocus() // 清除焦点避免键盘再次弹出 + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/LogUtil.kt b/app/src/main/java/com/shuwei/dish/match/utils/LogUtil.kt new file mode 100644 index 0000000..2d3a6ea --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/LogUtil.kt @@ -0,0 +1,11 @@ +package com.shuwei.dish.match.utils + +import android.util.Log + +//object LogUtil { +// +// fun d(tag: String, msg: String) { +// Log.d(tag, msg) +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/SwipeCallback.kt b/app/src/main/java/com/shuwei/dish/match/utils/SwipeCallback.kt new file mode 100644 index 0000000..cb1c384 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/SwipeCallback.kt @@ -0,0 +1,62 @@ +package com.shuwei.dish.match.utils + +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.adapter.DishPartAdapter +import com.shuwei.dish.match.adapter.DishShowAdapter +import com.shuwei.dish.match.adapter.SamplingAdapter + +class SwipeCallback( + private val adapter: BaseQuickAdapter<*,*>, + private val callback: (position: Int) -> Unit = {} +) : + ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + return false // 不允许拖动,只允许滑动删除 + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + val position = viewHolder.getBindingAdapterPosition() + callback(position) + } + + override fun getMovementFlags( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + val position = viewHolder.getBindingAdapterPosition() + // 默认的拖动和滑动方向 + val dragFlags = 0 + // ItemTouchHelper.UP or ItemTouchHelper.DOWN + var swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT + + if (adapter is SamplingAdapter) { + if (!adapter.items[position].isCooking) { + swipeFlags = 0 + } + } else if (adapter is DishShowAdapter) { + if (!adapter.items[position].isCooking) { + swipeFlags = 0 + } + } else if (adapter is DishPartAdapter) { + if (!adapter.items[position].isNewDishType) { + swipeFlags = 0 + } + } + + // 根据viewHolder的逻辑判断是否允许滑动 + val count = recyclerView.adapter?.itemCount ?:0 + //BaseQuickAdapter中空布局使用的ViewHolder是这个import com.chad.library.adapter4.viewholder.StateLayoutVH + if (count == 1 && viewHolder !is QuickViewHolder) { + swipeFlags = 0 // 不允许滑动 + } + return makeMovementFlags(dragFlags, swipeFlags) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/ToastUtil.kt b/app/src/main/java/com/shuwei/dish/match/utils/ToastUtil.kt new file mode 100644 index 0000000..e4e5bbe --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/ToastUtil.kt @@ -0,0 +1,28 @@ +package com.shuwei.dish.match.utils + +import android.content.Context +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import android.widget.Toast +import com.shuwei.dish.match.R + + +object ToastUtil { + + fun show(context: Context, message: String, duration:Int = Toast.LENGTH_SHORT) { + val inflater = LayoutInflater.from(context) + val layout: View = inflater.inflate(R.layout.custom_toast, null) + val textView = layout.findViewById(R.id.tvMessage) + textView.text = message + + val toast = Toast(context) + toast.view = layout // 设置自定义视图 + toast.setGravity(Gravity.CENTER, 0, 0) // 调整位置居中显示 + toast.setDuration(duration) + toast.show() + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/WeightUtil.kt b/app/src/main/java/com/shuwei/dish/match/utils/WeightUtil.kt new file mode 100644 index 0000000..531843f --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/WeightUtil.kt @@ -0,0 +1,148 @@ +package com.shuwei.dish.match.utils + +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.util.Log +import com.aithings.Weigher +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.utils.ext.toast +import com.wabon.wbintelligenthardwaresdk.api.SensorScale + +object WeightUtil { + + fun connect(initCallback: (connect: Boolean) -> Unit) { + Weigher.setListener(object : WeightListenerImpl() { + override fun onInit(connect: Boolean) { + initCallback(connect) + Log.d("WeightUtil", "connect=$connect") + } + + override fun onZero() { + Log.d("WeightUtil", "zero ok") + } + + override fun onTare() { + Log.d("WeightUtil", "去皮置零操作成功") + } + + override fun onSetIdentify() { + Log.d("WeightUtil", "鉴别率设置操作成功") + } + + override fun onGetWeight(address: Int, state: Int, weight: Int) { + runOnUiThread { + val stateStr = when (state) { + SensorScale.STATE_STABLE -> "稳定" + SensorScale.STATE_UNSTABLE -> "不稳定" + SensorScale.STATE_OVER_WEIGHT -> "量程溢出" + else -> "" + } + val result1 = String.format("$stateStr 重量:%s kg", weight / 1000f) + Log.d("WeightUtil", "ttlReturn, address:$address,$result1") + } + } + + override fun onReadIdentify(rate: Int) { + Log.d("WeightUtil", "读取鉴别率成功:rate=$rate") + } + + override fun onFail(errCode: Int) { + val str = when (errCode) { + Weigher.ERR_001 -> "电子称未初始化" + Weigher.ERR_002 -> "开机零位异常" + Weigher.ERR_003 -> "传感器故障" + Weigher.ERR_004 -> "鉴别率超出范围" + Weigher.ERR_PCB_NOT_SUPPORT -> "主板不支持, " + Build.MODEL + else -> "" + } + Log.d("WeightUtil", str) + } + + override fun onFastFilter() { + Log.d("WeightUtil", "快速滤波设置成功") + } + + override fun onReadParam() { + Log.d("WeightUtil", "prepare ok") + } + + override fun onRangCal() { + Log.d("WeightUtil", "rang cal ok") + } + + override fun onSetParam() { + Log.d("WeightUtil", "onSetParam") + } + }) + } + + private fun runOnUiThread(action: () -> Unit) { + Handler(Looper.getMainLooper()).post { + action() + } + } + + fun addWeightListener( + zero: (() -> Unit) = {}, + getWeight: ((address: Int, state: Int, weight: Int) -> Unit) = { _, _, _ -> } + ) { + Weigher.setListener(object : WeightListenerImpl() { + override fun onZero() { + zero() + } + + override fun onGetWeight(address: Int, state: Int, weight: Int) { + getWeight(address, state, weight) + } + }) + } + +} + +open class WeightListenerImpl : Weigher.Listener { + override fun onInit(connect: Boolean) { + + } + + override fun onZero() { + + } + + override fun onTare() { + + } + + override fun onSetIdentify() { + + } + + override fun onGetWeight(address: Int, state: Int, weight: Int) { + + } + + override fun onReadIdentify(rate: Int) { + + } + + override fun onFail(errCode: Int) { + + } + + override fun onFastFilter() { + + } + + override fun onReadParam() { + + } + + override fun onRangCal() { + + } + + override fun onSetParam() { + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/utils/ext/Common.kt b/app/src/main/java/com/shuwei/dish/match/utils/ext/Common.kt new file mode 100644 index 0000000..97c51d1 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/ext/Common.kt @@ -0,0 +1,175 @@ +package com.shuwei.dish.match.utils.ext + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.res.Resources +import android.os.Bundle +import android.util.TypedValue +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.Toast +import androidx.core.app.ActivityOptionsCompat +import androidx.fragment.app.Fragment +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.shuwei.dish.match.utils.ToastUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +fun Context.toast( + message: String?, + duration: Int = Toast.LENGTH_SHORT, +) { + if (message.isNullOrBlank()) { + return + } + //Toast.makeText(this, message, duration).show() + ToastUtil.show(this, message, duration) +} + +fun Fragment.toast( + message: String?, + duration: Int = 2000 +) { + activity?.toast(message, duration) +} + +//fun Fragment.toast( +// message: String?, +// duration: Int = 2000 +//) { +// if (isAdded.not()) { +// return +// } +// CustomToast( +// activity = requireActivity(), +// message = message, +// duration = duration +// ).show() +//} +// +//public fun BaseActivity.toast(message: String?, duration: Int = 2000) { +// CustomToast( +// activity = this, +// message = message, +// duration = duration +// ).show() +//} + +fun View.visible() { + visibility = View.VISIBLE +} + +fun View.invisible() { + visibility = View.INVISIBLE +} + +fun View.gone() { + visibility = View.GONE +} + +inline fun Context.startActivity( + bundle: Bundle? = null, + options: ActivityOptionsCompat? = null +) { + Intent(this, T::class.java).apply { + bundle?.let { putExtras(it) } + if (options != null && this@startActivity is Activity) { + startActivity(this, options.toBundle()) + } else { + startActivity(this) + } + } +} + +inline fun Context.startActivity( + block: Intent.() -> Unit = {} +) { + Intent(this, T::class.java).apply { + block() + startActivity(this) + } +} + +//inline fun Context.startActivity(action:(bundle: Bundle)-> Unit) { +// Intent(this, T::class.java).apply { +// action(Bundle()) +// startActivity(this) +// } +//} + +inline fun String.toType(gson: Gson? = null, typeToken: TypeToken): T { + return (gson ?: Gson()).fromJson(this, typeToken.type) +} + +inline fun String.toObject(gson: Gson? = null): T { + return (gson ?: Gson()).fromJson(this, T::class.java) +} + +fun Any?.toJsonString(gson: Gson? = null): String { + return (gson ?: Gson()).toJson(this) ?: "" +} + +@SuppressLint("ApplySharedPref") +inline fun SharedPreferences.edit( + commit: Boolean = false, + action: SharedPreferences.Editor.() -> Unit +) { + val editor = edit() + action(editor) + if (commit) editor.commit() else editor.apply() +} + +fun SharedPreferences.put(vararg pairs: Pair) { + edit { + pairs.forEach { (key, value) -> + when (value) { + is Int -> putInt(key, value) + is String -> putString(key, value) + is Boolean -> putBoolean(key, value) + is Float -> putFloat(key, value) + is Long -> putLong(key, value) + } + } + } +} + +val Float.dp: Float + get() = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + this, + Resources.getSystem().displayMetrics + ) + +val Int.dp: Int + get() = this.toFloat().dp.toInt() + +fun EditText.addOnActionSearchListener(searchCallback: () -> Unit) { + setOnEditorActionListener { v, actionId, event -> + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + // 处理搜索逻辑 + searchCallback() + return@setOnEditorActionListener true // 阻止事件继续传递 + } + return@setOnEditorActionListener false + } +} + + +fun View.clickWithDebounce(delay: Long = 500, action: () -> Unit) { + var job: Job? = null + setOnClickListener { + job?.cancel() + job = CoroutineScope(Dispatchers.Main).launch { + delay(delay) + action() + } + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/utils/ext/SpannableExt.kt b/app/src/main/java/com/shuwei/dish/match/utils/ext/SpannableExt.kt new file mode 100644 index 0000000..bfee7e8 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/ext/SpannableExt.kt @@ -0,0 +1,25 @@ +package com.shuwei.dish.match.utils.ext + +import android.text.SpannableString +import android.text.SpannableStringBuilder +import android.text.Spanned + + +inline fun buildSpannableString(builderAction: SpannableStringBuilder.() -> Unit): SpannableStringBuilder { + return SpannableStringBuilder().apply(builderAction) +} + +fun SpannableStringBuilder.appendText(text: String, vararg spans: Any): SpannableStringBuilder { + val start = length + append(text) + spans.forEach { span -> + setSpan(span, start, length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) + } + return this +} + +fun SpannableStringBuilder.withSpan(span: Any, block: SpannableStringBuilder.() -> Unit) { + val start = length + block() + setSpan(span, start, length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) +} diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt new file mode 100644 index 0000000..d7eff06 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt @@ -0,0 +1,183 @@ +package com.shuwei.dish.match.viewmodel + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.db.AppRepository +import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.utils.ext.toast +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlin.collections.forEach + +class AppViewModel(private val rep: AppRepository) : ViewModel() { + + fun updateCookFood(entity: CookFoodEntity) { + viewModelScope.launch { + rep.updateCookFood(entity) + } + } + + fun getCookFoodById(foodId: String, cookMode: Int, action: (CookFoodEntity?) -> Unit) { + viewModelScope.launch { + val data = rep.getCookFoodById(foodId, cookMode) + action(data) + } + } + + fun getCookFoodList(cookMode: Int, action: (MutableList?) -> Unit) { + viewModelScope.launch { + val data = rep.getCookFoodList(cookMode) + action(data) + } + } + + fun countCookFood(cookMode: Int, action: (Int) -> Unit) { + viewModelScope.launch { + val count = rep.countCookFood(cookMode) + action(count) + } + } + + fun saveCookFood(cookMode: Int, entity: CookFoodEntity) { + viewModelScope.launch { + val data = rep.getCookFoodById(entity.foodId, cookMode) + if (data == null) { + rep.insertCookFood(entity) + return@launch + } + rep.updateCookFood(entity) + } + } + + fun saveCookFoodAndGoods( + cookMode: Int, + entity: CookFoodEntity, + list: MutableList?, + onFinish: () -> Unit + ) { + viewModelScope.launch { + val data = rep.getCookFoodById(entity.foodId, cookMode) + if (data != null) { + //存在数据删除 + rep.deleteCookFood(entity.foodId, cookMode) + rep.deleteCookFoodGoodsList(entity.foodId) + } + //重新保存数据 + rep.insertCookFood(entity) + list?.let { rep.insertGoodsList(it) } + onFinish() + } + } + + fun deleteCookFoodAndGoods( + cookMode: Int, + foodId: String, + onFinish: () -> Unit + ) { + viewModelScope.launch { + rep.deleteCookFood(foodId = foodId, cookMode = cookMode) + rep.deleteCookFoodGoodsList(foodId) + onFinish() + } + } + + //-------------------------------------------------------------------------------------------------------- + + + fun getCookFoodGoodsList( + foodId: String, + action: (list: MutableList) -> Unit + ) { + viewModelScope.launch { + val list = rep.getCookFoodGoodsList(foodId) + action(list) + } + } + + fun updateGoods(entity: CookFoodGoodsEntity) { + viewModelScope.launch { + rep.updateGoods(entity) + } + } + + fun saveCookFoodGoods(list: MutableList) { + viewModelScope.launch { + val foodId = list[0].foodId + val data: MutableList? = + rep.getCookFoodGoodsList(foodId!!) + if (data.isNullOrEmpty()) { + rep.insertGoodsList(list) + return@launch + } + val localDbSeasoningList = rep.getCookFoodGoodsListByType(foodId, 3) + localDbSeasoningList.forEach { + it.isDel = 1 + //更新本地调料数据为已删除的状态 + rep.updateGoods(it) + } + //筛选出调料数据保存,主辅材不会变更不需要再次处理 + val saveSeasoningList = list.filter { it.materialType == 3 } + rep.insertGoodsList(saveSeasoningList.toMutableList()) + } + } + + //-------------------------------------------------------------------------------------------------------- + + fun addSeasoning(list: MutableList, callback: () -> Unit) { + viewModelScope.launch { + rep.deleteBatch() + rep.insertSeasoningList(items = list) + callback() + } + } + + private var isProcessing = false + fun loadSeasoning(action: (list: MutableList) -> Unit) { + viewModelScope.launch { + if (isProcessing) return@launch + isProcessing = true + rep.getAllStream().collect { list -> + action(list) + isProcessing = false + } + } + } + + fun deleteEmptySeasoning() { + viewModelScope.launch { + rep.deleteEmptySeasoning() + } + } + fun deleteAll() { + viewModelScope.launch { + rep.deleteBatch() + } + } + + fun updateAll(list: MutableList) { + viewModelScope.launch { + list.forEach { + rep.updateSeasoning(it) + } + } + } + + fun saveSeasoning(entity: SeasoningEntity, block:()-> Unit) { + viewModelScope.launch { + val seasoning = rep.getSeasoningBySort(entity.sort) + if (seasoning == null) { + rep.insertSeasoning(entity) + block() + return@launch + } + rep.updateSeasoning(entity.also { it.id = seasoning.id }) + block() + } + } + + //-------------------------------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt new file mode 100644 index 0000000..5322ca2 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt @@ -0,0 +1,42 @@ +//package com.shuwei.dish.match.viewmodel +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.shuwei.dish.match.db.CookFoodGoodsRepository +//import com.shuwei.dish.match.entity.CookFoodGoodsEntity +//import kotlinx.coroutines.launch +// +//class CookFoodGoodsViewModel(private val rep: CookFoodGoodsRepository) : ViewModel() { +// +// fun getCookFoodGoodsList( +// foodId: String, +// action: (list: MutableList) -> Unit +// ) { +// viewModelScope.launch { +// val list = rep.cookFoodGoodsDao.getCookFoodGoodsList(foodId) +// action(list) +// } +// } +// +// fun saveCookFoodGoods(list: MutableList) { +// viewModelScope.launch { +// val foodId = list[0].foodId +// val data: MutableList? = +// rep.cookFoodGoodsDao.getCookFoodGoodsList(foodId!!) +// if (data.isNullOrEmpty()) { +// rep.cookFoodGoodsDao.insertAll(list) +// return@launch +// } +// val localDbSeasoningList = rep.cookFoodGoodsDao.getCookFoodGoodsListByType(foodId, 3) +// localDbSeasoningList.forEach { +// it.isDel = 1 +// //更新本地调料数据为已删除的状态 +// rep.cookFoodGoodsDao.update(it) +// } +// //筛选出调料数据保存,主辅材不会变更不需要再次处理 +// val saveSeasoningList = list.filter { it.materialType == 3 } +// rep.cookFoodGoodsDao.insertAll(saveSeasoningList.toMutableList()) +// } +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt new file mode 100644 index 0000000..a6c8468 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt @@ -0,0 +1,44 @@ +//package com.shuwei.dish.match.viewmodel +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.shuwei.dish.match.db.CookFoodRepository +//import com.shuwei.dish.match.entity.CookFoodEntity +//import kotlinx.coroutines.launch +// +//class CookFoodViewModel(private val rep: CookFoodRepository) : ViewModel() { +// +// fun getCookFoodById(foodId: String, cookMode: Int, action: (CookFoodEntity?) -> Unit) { +// viewModelScope.launch { +// val data = rep.cookFoodDao.getCookFoodById(foodId, cookMode) +// action(data) +// } +// } +// +// fun getCookFoodList(cookMode: Int, action: (MutableList?) -> Unit) { +// viewModelScope.launch { +// val data = rep.cookFoodDao.getCookFoodList(cookMode) +// action(data) +// } +// } +// +// fun countCookFood(cookMode: Int, action: (Int) -> Unit) { +// viewModelScope.launch { +// val count = rep.cookFoodDao.countCookFood(cookMode) +// action(count) +// } +// } +// +// fun saveCookFood(cookMode: Int, entity: CookFoodEntity) { +// viewModelScope.launch { +// val data = rep.cookFoodDao.getCookFoodById(entity.foodId, cookMode) +// if (data == null) { +// rep.cookFoodDao.insert(entity) +// return@launch +// } +// rep.cookFoodDao.update(entity) +// +// } +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt new file mode 100644 index 0000000..cbb8e0d --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt @@ -0,0 +1,47 @@ +//package com.shuwei.dish.match.viewmodel +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.shuwei.dish.match.db.SeasoningRepository +//import com.shuwei.dish.match.entity.SeasoningEntity +//import kotlinx.coroutines.delay +//import kotlinx.coroutines.launch +// +//class DeviceSettingViewModel(private val rep: SeasoningRepository) : ViewModel() { +// fun addSeasoning(list: MutableList, callback:()-> Unit) { +// viewModelScope.launch { +// rep.seasoningDao.deleteBatch() +// delay(500) +// val rows: Array? = rep.seasoningDao.insertAll(items = list) +// if (rows.isNullOrEmpty().not()) { +// callback() +// } +// } +// } +// +// private var isProcessing = false +// fun loadSeasoning(action: (list: MutableList) -> Unit) { +// viewModelScope.launch { +// if (isProcessing) return@launch +// isProcessing = true +// rep.seasoningDao.getAllStream().collect { list -> +// action(list) +// isProcessing = false +// } +// } +// } +// +// fun deleteAll() { +// viewModelScope.launch { +// rep.seasoningDao.deleteBatch() +// } +// } +// +// fun updateAll(list: MutableList) { +// viewModelScope.launch { +// list.forEach { +// rep.seasoningDao.update(it) +// } +// } +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt new file mode 100644 index 0000000..2919895 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt @@ -0,0 +1,24 @@ +//package com.shuwei.dish.match.viewmodel +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.shuwei.dish.match.db.SeasoningRepository +//import com.shuwei.dish.match.entity.CookFoodEntity +//import com.shuwei.dish.match.entity.SeasoningEntity +//import kotlinx.coroutines.launch +// +//class SubmitDishViewModel(private val rep: SeasoningRepository) : ViewModel() { +// +// private var isProcessing = false +// fun loadSeasoning(action: (list: MutableList) -> Unit) { +// viewModelScope.launch { +// if (isProcessing) return@launch +// isProcessing = true +// rep.seasoningDao.getAllStream().collect { list -> +// action(list) +// isProcessing = false +// } +// } +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt new file mode 100644 index 0000000..1ca32fc --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt @@ -0,0 +1,43 @@ +package com.shuwei.dish.match.viewmodel.factory + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.shuwei.dish.match.db.AppRepository +import com.shuwei.dish.match.viewmodel.AppViewModel + +@Suppress("UNCHECKED_CAST") +class AppFactory(private val repo: AppRepository) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return AppViewModel(repo) as T + } +} + +// +//@Suppress("UNCHECKED_CAST") +//class DeviceSettingFactory(private val repo: SeasoningRepository) : ViewModelProvider.Factory { +// override fun create(modelClass: Class): T { +// return DeviceSettingViewModel(repo) as T +// } +//} +// +////@Suppress("UNCHECKED_CAST") +////class SubmitDishFactory(private val repo: SeasoningRepository) : ViewModelProvider.Factory { +//// override fun create(modelClass: Class): T { +//// return SubmitDishViewModel(repo) as T +//// } +////} +// +//@Suppress("UNCHECKED_CAST") +//class CookFoodFactory(private val repo: CookFoodRepository) : ViewModelProvider.Factory { +// override fun create(modelClass: Class): T { +// return CookFoodViewModel(repo) as T +// } +//} +// +//@Suppress("UNCHECKED_CAST") +//class CookFoodGoodsFactory(private val repo: CookFoodGoodsRepository) : ViewModelProvider.Factory { +// override fun create(modelClass: Class): T { +// return CookFoodGoodsViewModel(repo) as T +// } +//} + diff --git a/app/src/main/res/color/breakfast_font.xml b/app/src/main/res/color/breakfast_font.xml new file mode 100644 index 0000000..197f200 --- /dev/null +++ b/app/src/main/res/color/breakfast_font.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/dish_type_font.xml b/app/src/main/res/color/dish_type_font.xml new file mode 100644 index 0000000..35199a3 --- /dev/null +++ b/app/src/main/res/color/dish_type_font.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xxxhdpi/bg_home_page.png b/app/src/main/res/drawable-xxxhdpi/bg_home_page.png new file mode 100644 index 0000000..cab6ff2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bg_home_page.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_right.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_right.png new file mode 100644 index 0000000..324a23b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_right.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_back.png b/app/src/main/res/drawable-xxxhdpi/ic_back.png new file mode 100644 index 0000000..e01068e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_back.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_check_false.png b/app/src/main/res/drawable-xxxhdpi/ic_check_false.png new file mode 100644 index 0000000..7b02c5c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_check_false.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_check_true.png b/app/src/main/res/drawable-xxxhdpi/ic_check_true.png new file mode 100644 index 0000000..f5b5b6d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_check_true.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_delete.png b/app/src/main/res/drawable-xxxhdpi/ic_delete.png new file mode 100644 index 0000000..f5a14db Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_delete.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dish_disable.png b/app/src/main/res/drawable-xxxhdpi/ic_dish_disable.png new file mode 100644 index 0000000..fc92f86 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dish_disable.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dish_selected.png b/app/src/main/res/drawable-xxxhdpi/ic_dish_selected.png new file mode 100644 index 0000000..3ec01f4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dish_selected.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dish_unselected.png b/app/src/main/res/drawable-xxxhdpi/ic_dish_unselected.png new file mode 100644 index 0000000..6754bbc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dish_unselected.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_food_add.png b/app/src/main/res/drawable-xxxhdpi/ic_food_add.png new file mode 100644 index 0000000..92002b6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_food_add.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_lock.png b/app/src/main/res/drawable-xxxhdpi/ic_lock.png new file mode 100644 index 0000000..93212d4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_lock.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_mode_cook.png b/app/src/main/res/drawable-xxxhdpi/ic_mode_cook.png new file mode 100644 index 0000000..0ac4e9e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_mode_cook.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_mode_quality.png b/app/src/main/res/drawable-xxxhdpi/ic_mode_quality.png new file mode 100644 index 0000000..373b64b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_mode_quality.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_mode_sampling.png b/app/src/main/res/drawable-xxxhdpi/ic_mode_sampling.png new file mode 100644 index 0000000..be50c41 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_mode_sampling.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search.png b/app/src/main/res/drawable-xxxhdpi/ic_search.png new file mode 100644 index 0000000..cbad3e4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_gray.png b/app/src/main/res/drawable-xxxhdpi/ic_search_gray.png new file mode 100644 index 0000000..6aa389f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search_gray.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_search_green.png b/app/src/main/res/drawable-xxxhdpi/ic_search_green.png new file mode 100644 index 0000000..de61c79 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_search_green.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_setting.png b/app/src/main/res/drawable-xxxhdpi/ic_setting.png new file mode 100644 index 0000000..5631615 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_setting.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_weight_add.png b/app/src/main/res/drawable-xxxhdpi/ic_weight_add.png new file mode 100644 index 0000000..73ff568 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_weight_add.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_weight_clear.png b/app/src/main/res/drawable-xxxhdpi/ic_weight_clear.png new file mode 100644 index 0000000..6573802 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_weight_clear.png differ diff --git a/app/src/main/res/drawable/bg_other_page.xml b/app/src/main/res/drawable/bg_other_page.xml new file mode 100644 index 0000000..8ef2112 --- /dev/null +++ b/app/src/main/res/drawable/bg_other_page.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_rounded_top.xml b/app/src/main/res/drawable/bg_rounded_top.xml new file mode 100644 index 0000000..0f8bc89 --- /dev/null +++ b/app/src/main/res/drawable/bg_rounded_top.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/breakfast_bg.xml b/app/src/main/res/drawable/breakfast_bg.xml new file mode 100644 index 0000000..af19580 --- /dev/null +++ b/app/src/main/res/drawable/breakfast_bg.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/mode_select.xml b/app/src/main/res/drawable/mode_select.xml new file mode 100644 index 0000000..4d37800 --- /dev/null +++ b/app/src/main/res/drawable/mode_select.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ripple_effect_green.xml b/app/src/main/res/drawable/ripple_effect_green.xml new file mode 100644 index 0000000..98e073c --- /dev/null +++ b/app/src/main/res/drawable/ripple_effect_green.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ripple_effect_light.xml b/app/src/main/res/drawable/ripple_effect_light.xml new file mode 100644 index 0000000..0c723c6 --- /dev/null +++ b/app/src/main/res/drawable/ripple_effect_light.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ripple_effect_light2.xml b/app/src/main/res/drawable/ripple_effect_light2.xml new file mode 100644 index 0000000..dbe43a0 --- /dev/null +++ b/app/src/main/res/drawable/ripple_effect_light2.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/selector_dish_type.xml b/app/src/main/res/drawable/selector_dish_type.xml new file mode 100644 index 0000000..7998612 --- /dev/null +++ b/app/src/main/res/drawable/selector_dish_type.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_text_cell.xml b/app/src/main/res/drawable/selector_text_cell.xml new file mode 100644 index 0000000..1f07d0a --- /dev/null +++ b/app/src/main/res/drawable/selector_text_cell.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_dialog.xml b/app/src/main/res/drawable/shape_dialog.xml new file mode 100644 index 0000000..c7e4546 --- /dev/null +++ b/app/src/main/res/drawable/shape_dialog.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_gray_dc_5.xml b/app/src/main/res/drawable/shape_gray_dc_5.xml new file mode 100644 index 0000000..633252c --- /dev/null +++ b/app/src/main/res/drawable/shape_gray_dc_5.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_gray_f8_15.xml b/app/src/main/res/drawable/shape_gray_f8_15.xml new file mode 100644 index 0000000..9319794 --- /dev/null +++ b/app/src/main/res/drawable/shape_gray_f8_15.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_green_15_corners.xml b/app/src/main/res/drawable/shape_green_15_corners.xml new file mode 100644 index 0000000..4b0ff0e --- /dev/null +++ b/app/src/main/res/drawable/shape_green_15_corners.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_green_5_corners.xml b/app/src/main/res/drawable/shape_green_5_corners.xml new file mode 100644 index 0000000..ad634e9 --- /dev/null +++ b/app/src/main/res/drawable/shape_green_5_corners.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_green_bg.xml b/app/src/main/res/drawable/shape_green_bg.xml new file mode 100644 index 0000000..1f9fd2f --- /dev/null +++ b/app/src/main/res/drawable/shape_green_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_green_stroke.xml b/app/src/main/res/drawable/shape_green_stroke.xml new file mode 100644 index 0000000..dba4385 --- /dev/null +++ b/app/src/main/res/drawable/shape_green_stroke.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_item_cook_dish.xml b/app/src/main/res/drawable/shape_item_cook_dish.xml new file mode 100644 index 0000000..723b9b6 --- /dev/null +++ b/app/src/main/res/drawable/shape_item_cook_dish.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_12_corners.xml b/app/src/main/res/drawable/shape_white_12_corners.xml new file mode 100644 index 0000000..868628f --- /dev/null +++ b/app/src/main/res/drawable/shape_white_12_corners.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_12_corners2.xml b/app/src/main/res/drawable/shape_white_12_corners2.xml new file mode 100644 index 0000000..d1285c4 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_12_corners2.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_30_corners.xml b/app/src/main/res/drawable/shape_white_30_corners.xml new file mode 100644 index 0000000..a6e1ed5 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_30_corners.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_dash_15_corners.xml b/app/src/main/res/drawable/shape_white_dash_15_corners.xml new file mode 100644 index 0000000..3933de3 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_dash_15_corners.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_e6_12_corners.xml b/app/src/main/res/drawable/shape_white_e6_12_corners.xml new file mode 100644 index 0000000..b0e23f4 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_e6_12_corners.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_f6_10_corners.xml b/app/src/main/res/drawable/shape_white_f6_10_corners.xml new file mode 100644 index 0000000..6a5f42f --- /dev/null +++ b/app/src/main/res/drawable/shape_white_f6_10_corners.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_fb_15_corners.xml b/app/src/main/res/drawable/shape_white_fb_15_corners.xml new file mode 100644 index 0000000..475fcc8 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_fb_15_corners.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_fb_15_corners2.xml b/app/src/main/res/drawable/shape_white_fb_15_corners2.xml new file mode 100644 index 0000000..05e1a64 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_fb_15_corners2.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_base.xml b/app/src/main/res/layout/activity_base.xml new file mode 100644 index 0000000..9da7115 --- /dev/null +++ b/app/src/main/res/layout/activity_base.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_device_setting.xml b/app/src/main/res/layout/activity_device_setting.xml new file mode 100644 index 0000000..90c2c03 --- /dev/null +++ b/app/src/main/res/layout/activity_device_setting.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_food_search.xml b/app/src/main/res/layout/activity_food_search.xml new file mode 100644 index 0000000..a2115b7 --- /dev/null +++ b/app/src/main/res/layout/activity_food_search.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml new file mode 100644 index 0000000..0f82f14 --- /dev/null +++ b/app/src/main/res/layout/activity_home.xml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_prepare_cook.xml b/app/src/main/res/layout/activity_prepare_cook.xml new file mode 100644 index 0000000..144f63d --- /dev/null +++ b/app/src/main/res/layout/activity_prepare_cook.xml @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_sampling.xml b/app/src/main/res/layout/activity_sampling.xml new file mode 100644 index 0000000..cac23c6 --- /dev/null +++ b/app/src/main/res/layout/activity_sampling.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_select_dish.xml b/app/src/main/res/layout/activity_select_dish.xml new file mode 100644 index 0000000..39a1a17 --- /dev/null +++ b/app/src/main/res/layout/activity_select_dish.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_submit_dish.xml b/app/src/main/res/layout/activity_submit_dish.xml new file mode 100644 index 0000000..6f37f38 --- /dev/null +++ b/app/src/main/res/layout/activity_submit_dish.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/custom_toast.xml b/app/src/main/res/layout/custom_toast.xml new file mode 100644 index 0000000..2deaaa4 --- /dev/null +++ b/app/src/main/res/layout/custom_toast.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_bottom_sheet.xml b/app/src/main/res/layout/dialog_bottom_sheet.xml new file mode 100644 index 0000000..59f8bc2 --- /dev/null +++ b/app/src/main/res/layout/dialog_bottom_sheet.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_bottom_sheet2.xml b/app/src/main/res/layout/dialog_bottom_sheet2.xml new file mode 100644 index 0000000..98858e6 --- /dev/null +++ b/app/src/main/res/layout/dialog_bottom_sheet2.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_common.xml b/app/src/main/res/layout/dialog_common.xml new file mode 100644 index 0000000..d64c3b5 --- /dev/null +++ b/app/src/main/res/layout/dialog_common.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 0000000..67410fa --- /dev/null +++ b/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dish_list.xml b/app/src/main/res/layout/fragment_dish_list.xml new file mode 100644 index 0000000..1e76c82 --- /dev/null +++ b/app/src/main/res/layout/fragment_dish_list.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dish_sampling.xml b/app/src/main/res/layout/fragment_dish_sampling.xml new file mode 100644 index 0000000..390c42a --- /dev/null +++ b/app/src/main/res/layout/fragment_dish_sampling.xml @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_sampling_list.xml b/app/src/main/res/layout/fragment_sampling_list.xml new file mode 100644 index 0000000..3871be0 --- /dev/null +++ b/app/src/main/res/layout/fragment_sampling_list.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_empty_view.xml b/app/src/main/res/layout/layout_empty_view.xml new file mode 100644 index 0000000..2a5757d --- /dev/null +++ b/app/src/main/res/layout/layout_empty_view.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_food_remind.xml b/app/src/main/res/layout/layout_food_remind.xml new file mode 100644 index 0000000..1f3369c --- /dev/null +++ b/app/src/main/res/layout/layout_food_remind.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_cell.xml b/app/src/main/res/layout/list_item_cell.xml new file mode 100644 index 0000000..71c0e23 --- /dev/null +++ b/app/src/main/res/layout/list_item_cell.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_dish.xml b/app/src/main/res/layout/list_item_dish.xml new file mode 100644 index 0000000..c79b06b --- /dev/null +++ b/app/src/main/res/layout/list_item_dish.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_dish_cook.xml b/app/src/main/res/layout/list_item_dish_cook.xml new file mode 100644 index 0000000..a9cca94 --- /dev/null +++ b/app/src/main/res/layout/list_item_dish_cook.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_dish_record.xml b/app/src/main/res/layout/list_item_dish_record.xml new file mode 100644 index 0000000..df63d85 --- /dev/null +++ b/app/src/main/res/layout/list_item_dish_record.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_home_mode.xml b/app/src/main/res/layout/list_item_home_mode.xml new file mode 100644 index 0000000..c59199f --- /dev/null +++ b/app/src/main/res/layout/list_item_home_mode.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_search_goods_info.xml b/app/src/main/res/layout/list_item_search_goods_info.xml new file mode 100644 index 0000000..1da3490 --- /dev/null +++ b/app/src/main/res/layout/list_item_search_goods_info.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..8fdd780 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..2762795 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,33 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + + #F0F5FF + #FF000A2D + #FF646478 + #FF141414 + #FF8B8B8B + #FFABADAC + #FFABADAC + #FF646478 + #B0BEC5 + #30000000 + #666666 + #999999 + #FBFBFB + #F6F6F6 + #00BC71 + #EBEBEB + #D6D6D6 + #B4B4B4 + #333333 + #C8C8C8 + #E6E6E6 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ed82680 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,38 @@ + + 菜品配比终端 + dish_match_terminal + 菜品配比终端 + 选择菜品模式 + 制作模式 + 菜品快速制作,记录熟重 + 采样模式 + 菜品快速制作,记录熟重 + 品控模式 + 菜品快速制作,记录熟重 + - 即将解锁 - + 早餐 + 午餐 + 晚餐 + 菜品列表 + 用量(克) + 在下方列表中选择食材计重 + 菜品构成 + 去制作 + 当前食材与菜品构成不一致 + 是否按当前食材制作或调整食材后再制作 + 调料 + 调料设置 + 点击分格配置分配调料 + 输入调料名称 + 烹饪中 + 已采样 + 输入菜品名称或速记码 + 输入菜品名称 + 输入食材名称 + 食材名称 + 主辅材 + 辅材 + 主材 + 新增采样 + 添加食材 + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..58d32ad --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..4df9255 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..8250474 --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,7 @@ + + + + vip.shuziweidao.com + 192.168.1.207 + + diff --git a/app/src/test/java/com/shuwei/dish/match/ExampleUnitTest.kt b/app/src/test/java/com/shuwei/dish/match/ExampleUnitTest.kt new file mode 100644 index 0000000..8ea083f --- /dev/null +++ b/app/src/test/java/com/shuwei/dish/match/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.shuwei.dish.match + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..922f551 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..20e2a01 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..df0c2e9 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,23 @@ +[versions] +agp = "8.11.1" +baserecyclerviewadapterhelper4 = "4.1.7" +kotlin = "2.0.21" +coreKtx = "1.10.1" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +appcompat = "1.6.1" +material = "1.10.0" + +[libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..379cb0f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jul 17 15:32:57 CST 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..c0c01d9 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,27 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + maven { url = uri("https://jitpack.io") } + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { url = uri("https://maven.aliyun.com/repository/public") } + maven { url = uri("https://jitpack.io") } + } +} + +rootProject.name = "DishMatch" +include(":app") + \ No newline at end of file diff --git a/system.keystore b/system.keystore new file mode 100644 index 0000000..959c9b8 Binary files /dev/null and b/system.keystore differ