CookDetailViewModel.kt 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. package com.develop.step.viewmodel
  2. import android.text.TextUtils
  3. import android.util.Log
  4. import androidx.lifecycle.LiveData
  5. import androidx.lifecycle.MutableLiveData
  6. import androidx.lifecycle.scopeNetLife
  7. import com.blankj.utilcode.util.FileUtils
  8. import com.blankj.utilcode.util.GsonUtils
  9. import com.blankj.utilcode.util.ToastUtils
  10. import com.blankj.utilcode.util.ZipUtils
  11. import com.develop.base.ext.globalApp
  12. import com.develop.base.mvvm.BaseViewModel
  13. import com.develop.base.util.FileKit
  14. import com.develop.base.util.MMkvUtils
  15. import com.develop.base.util.ThreadUtils
  16. import com.develop.common.bean.TranslateBean
  17. import com.develop.common.data_repo.FoodDataProvider
  18. import com.develop.common.data_repo.db.entity.DevAccessory
  19. import com.develop.common.data_repo.db.entity.DevRecipeCategory
  20. import com.develop.common.data_repo.db.entity.DevRecipeCookingStep
  21. import com.develop.common.data_repo.db.entity.DevRecipePortionSize
  22. import com.develop.common.data_repo.db.entity.UserOnLineRecipes
  23. import com.develop.common.data_repo.net.Api
  24. import com.develop.common.data_repo.net.model.response.RecipeDataConfig
  25. import com.develop.common.data_repo.net.model.response.RecipeDetailResult
  26. import com.develop.common.tag.CURRENT_LANGUAGE
  27. import com.develop.common.tag.CURRENT_USER_ID
  28. import com.develop.common.utils.Resource
  29. import com.develop.step.ui.recipes_detail.model.CookDetailInfo
  30. import com.develop.step.ui.recipes_detail.model.CookDetailTranslateBean
  31. import com.drake.net.Get
  32. import com.drake.net.Post
  33. import com.drake.net.component.Progress
  34. import com.drake.net.interfaces.ProgressListener
  35. import org.json.JSONObject
  36. import java.io.File
  37. class CookDetailViewModel : BaseViewModel() {
  38. var recipeLiveData = MutableLiveData<CookDetailInfo>()
  39. private val errorLiveData = MutableLiveData<Int>()
  40. val starCountLiveData = MutableLiveData<Int>()
  41. val downloadResource = MutableLiveData<Resource<Int>>()
  42. var portionSizeLiveData = MutableLiveData<DevRecipePortionSize>()
  43. var recipeNumber: String? = null
  44. private var mRecipeUrl: String? = null
  45. var cookDetailInfo :CookDetailInfo? = null
  46. //原始数据,用于做翻译功能
  47. var translateInfoInit : CookDetailTranslateBean? = null
  48. var cookingStep = mutableListOf<DevRecipeCookingStep>()
  49. var translateCookingStep = mutableListOf<DevRecipeCookingStep>()
  50. //是否线上菜谱
  51. var isRemote = false
  52. var language = MMkvUtils.getString(CURRENT_LANGUAGE) ?: "en"
  53. /**
  54. * 数据源: 食谱编号
  55. * 关联数据: [DevRecipe] 食谱基本数据
  56. * [DevRecipeAccessory] 食谱需要的配件
  57. * [DevAccessory] 单个配件数据
  58. * [DevRecipeFood] 食谱原料数据
  59. * [DevRecipeNutrition] 食谱营养数据
  60. * [DevRecipePortionSize] 食谱原料用量
  61. */
  62. fun queryRecipe(number: String, remote: Boolean, recipesEdition: String) {
  63. isRemote = remote
  64. this.recipeNumber = number
  65. if (remote) {
  66. scopeNetLife {
  67. Get<RecipeDetailResult>(Api.GET_RECIPES_DETAIL) {
  68. addQuery("recipeNumber", number)
  69. // addQuery("recipeEditon", recipesEdition)
  70. addQuery("lang", MMkvUtils.getString(CURRENT_LANGUAGE) ?: "EN")
  71. }.await().apply {
  72. mRecipeUrl = recipeUrl
  73. cookDetailInfo = CookDetailInfo(
  74. devRecipe,
  75. devAccessorys,
  76. devRecipeFoods,
  77. devRecipeNutritions,
  78. devRecipePortionSizes
  79. )
  80. //由于对象赋值问题,对象内存地址共用,估现在先转json,在重新赋值给初始实体
  81. var json = GsonUtils.toJson(cookDetailInfo)
  82. translateInfoInit = GsonUtils.fromJson(json,CookDetailTranslateBean::class.java)
  83. portionSizeLiveData.postValue(cookDetailInfo?.portionSize?.firstOrNull())
  84. recipeLiveData.postValue(cookDetailInfo)
  85. }
  86. }.catch {
  87. ToastUtils.showShort(it.message)
  88. }
  89. if (FoodDataProvider.isResourceDownload(number)) {
  90. downloadResource.value = Resource(100, Resource.Status.SUCCESS)
  91. }
  92. } else {
  93. FoodDataProvider.getDatabase().runInTransaction {
  94. var recipeDao = FoodDataProvider.getDatabase().recipeDao()
  95. var recipeBean = recipeDao.queryRecipe(number)
  96. if (recipeBean == null) {
  97. // show toast
  98. errorLiveData.postValue(ERR_NO_RECIPE_MATCH)
  99. return@runInTransaction
  100. }
  101. var includeAccessory = mutableListOf<DevAccessory>()
  102. var accessoryIds = recipeDao.queryAccessoryIds(number)
  103. for (accessoryId in accessoryIds) {
  104. val accessory = recipeDao.queryAccessory(accessoryId.accessoryNumber ?: "")
  105. if (accessory != null) {
  106. includeAccessory.add(accessory)
  107. }
  108. }
  109. var includeMaterial = recipeDao.queryFood(number)
  110. var includeNutrition = recipeDao.queryNutrition(number)
  111. var includePortionSize = recipeDao.queryPortionSize(number)
  112. cookDetailInfo = CookDetailInfo(
  113. recipeBean,
  114. includeAccessory,
  115. includeMaterial,
  116. includeNutrition,
  117. includePortionSize
  118. )
  119. //由于对象赋值问题,对象内存地址共用,估现在先转json,在重新赋值给初始实体
  120. var json = GsonUtils.toJson(cookDetailInfo)
  121. translateInfoInit = GsonUtils.fromJson(json,CookDetailTranslateBean::class.java)
  122. portionSizeLiveData.postValue(cookDetailInfo?.portionSize?.firstOrNull())
  123. recipeLiveData.postValue(cookDetailInfo)
  124. }
  125. }
  126. }
  127. fun getTranslateDetail(jsons :String,value: String){
  128. scopeNetLife {
  129. Post<String>(Api.TRANSLATE) {
  130. json(jsons)
  131. }.await().apply {
  132. //由于后端返回不规则key ,需特殊处理
  133. jsonTranslate( this,value)
  134. }
  135. }.catch {
  136. Log.e("TAG message",it.message.toString())
  137. ToastUtils.showShort(it.message)
  138. }
  139. }
  140. fun jsonTranslate (jsons: String,value: String){
  141. language = value
  142. var jsonObject = JSONObject(jsons)
  143. var jsonArray = jsonObject.getJSONArray(value)
  144. var size = jsonArray.length()-1
  145. for (i in 0 .. size){
  146. var jsonObject = jsonArray.getJSONObject(i)
  147. var id = jsonObject.getString("id")
  148. var valueList = jsonObject.getJSONArray("value")
  149. var value = valueList.getString(0)
  150. if (id=="name"){
  151. cookDetailInfo?.recipe?.name = value
  152. }
  153. if (id=="introduction"){
  154. cookDetailInfo?.recipe?.introduction = value
  155. }
  156. if (id=="difficulty"){
  157. cookDetailInfo?.recipe?.difficultyLevel = value
  158. }
  159. if (id.contains("foodname")){
  160. var ids = id
  161. var list = ids.split("_")
  162. var index = list[1].toInt()
  163. cookDetailInfo?.material?.get(index)?.foodName = value
  164. }
  165. if (id.contains("cookstep")){
  166. var ids = id
  167. var list = ids.split("_")
  168. var index = list[1].toInt()
  169. translateCookingStep[index].description = value
  170. }
  171. }
  172. portionSizeLiveData.postValue(cookDetailInfo?.portionSize?.firstOrNull())
  173. recipeLiveData.postValue(cookDetailInfo)
  174. }
  175. fun setTranslateDate(value :String):String{
  176. //需要添加步骤到翻译,由于菜谱详情没有获取,估在这重新获取
  177. //只获取一次,避免多次获取
  178. if (cookingStep.size<=0){
  179. cookingStep = FoodDataProvider
  180. .getDatabase()
  181. .recipeDao()
  182. .queryCookingStep(recipeNumber?:"").toMutableList()
  183. translateCookingStep = FoodDataProvider
  184. .getDatabase()
  185. .recipeDao()
  186. .queryCookingStep(recipeNumber?:"").toMutableList()
  187. }
  188. language = MMkvUtils.getString(CURRENT_LANGUAGE) ?:"en"
  189. var listLanguage = mutableListOf<String>()
  190. listLanguage.add(value)
  191. var translateList = mutableListOf<TranslateBean>()
  192. translateList.add(TranslateBean("name",translateInfoInit?.recipe?.name))
  193. translateList.add(TranslateBean("introduction",translateInfoInit?.recipe?.introduction))
  194. translateList.add(TranslateBean("difficulty",translateInfoInit?.recipe?.difficultyLevel))
  195. var size = translateInfoInit?.material?.size?:0
  196. if (size>0){
  197. size--
  198. }
  199. for (i in 0..size){
  200. // DevRecipeFood
  201. var material = translateInfoInit?.material?.get(i)
  202. material?.let {
  203. translateList.add(TranslateBean("foodname_"+i, it.foodName ?: ""))
  204. }
  205. }
  206. var sizeStep = cookingStep.size
  207. if (sizeStep>0){
  208. sizeStep--
  209. }
  210. for (i in 0..sizeStep){
  211. var cookingStep = cookingStep[i]
  212. cookingStep?.let {
  213. if (!TextUtils.isEmpty(it.description)){
  214. translateList.add(TranslateBean("cookstep_"+i, it.description ?: ""))
  215. }
  216. }
  217. }
  218. var map = HashMap<String,Any>()
  219. map["from"] = language.lowercase()
  220. map["tos"] = listLanguage
  221. map["translateList"] = translateList
  222. var json = GsonUtils.toJson(map)
  223. return json
  224. }
  225. fun getRecipeLiveData(): LiveData<CookDetailInfo> {
  226. return recipeLiveData
  227. }
  228. fun getErrorCodeLiveData(): LiveData<Int> {
  229. return errorLiveData
  230. }
  231. fun getStarCount(recipesId: String) {
  232. FoodDataProvider.getUserDatabase().runInTransaction {
  233. val userTag = FoodDataProvider.getUserDatabase().userInfoDao().queryUserTag(
  234. CURRENT_USER_ID, recipesId
  235. )
  236. userTag?.apply {
  237. starCountLiveData.postValue(starCount)
  238. }
  239. }
  240. }
  241. fun isDownloading(): Boolean {
  242. return downloadResource.value?.status == Resource.Status.LOADING
  243. }
  244. fun isDownloadSuccess(): Boolean {
  245. return downloadResource.value?.status == Resource.Status.SUCCESS
  246. }
  247. fun getDownloadState(): LiveData<Resource<Int>> {
  248. return downloadResource
  249. }
  250. fun downloadRecipe() {
  251. if (mRecipeUrl.isNullOrEmpty()) {
  252. downloadResource.value = Resource(0, Resource.Status.FAILURE)
  253. return
  254. }
  255. downloadResource.value = Resource(0, Resource.Status.LOADING)
  256. val downloadDir = globalApp().externalCacheDir.toString()
  257. val downloadName = System.nanoTime().toString()
  258. scopeNetLife {
  259. mRecipeUrl?.apply {
  260. val result = Get<File>(this) {
  261. setDownloadFileName(downloadName)
  262. setDownloadDir(downloadDir)
  263. addDownloadListener(object : ProgressListener() {
  264. override fun onProgress(p: Progress) {
  265. ThreadUtils.runOnMainThread {
  266. downloadResource.value =
  267. Resource(p.progress(), Resource.Status.LOADING)
  268. }
  269. }
  270. })
  271. }.await()
  272. if (!result.exists()) {
  273. ThreadUtils.runOnMainThread {
  274. downloadResource.value = Resource(0, Resource.Status.FAILURE)
  275. }
  276. } else {
  277. prepareResource(result)
  278. }
  279. }
  280. }.catch {
  281. ThreadUtils.runOnMainThread {
  282. downloadResource.value = Resource(0, Resource.Status.FAILURE)
  283. }
  284. }
  285. }
  286. private fun prepareResource(file: File) {
  287. try {
  288. val dst = ZipUtils.unzipFile(file, FoodDataProvider.getExtRecipeResourceDir())
  289. FileUtils.delete(file)
  290. if (dst.isNullOrEmpty()) {
  291. ThreadUtils.runOnMainThread {
  292. downloadResource.value = Resource(0, Resource.Status.FAILURE)
  293. }
  294. return
  295. }
  296. val jsonFile = FoodDataProvider.getResourceConfigJsonPath(recipeNumber!!)
  297. if (!jsonFile.exists()) {
  298. ThreadUtils.runOnMainThread {
  299. downloadResource.value = Resource(0, Resource.Status.FAILURE)
  300. }
  301. return
  302. }
  303. val jsonContent = FileKit.readFileToStringB(jsonFile)
  304. val contentData = GsonUtils.fromJson(jsonContent, RecipeDataConfig::class.java)
  305. // contentData.resetAllCodes()
  306. FoodDataProvider.getDatabase().runInTransaction {
  307. FoodDataProvider.getDatabase().recipeDao().apply {
  308. val categorys = queryAllCategory()
  309. val categoryMap = HashMap<String, DevRecipeCategory>()
  310. for (category in categorys) {
  311. categoryMap[category.number + ":" + category.lang] = category
  312. }
  313. for (devRecipeCategory in contentData.devRecipeCategorys) {
  314. if (categoryMap.containsKey(devRecipeCategory.number + ":" + devRecipeCategory.lang)) {
  315. devRecipeCategory.code =
  316. categoryMap[devRecipeCategory.number + ":" + devRecipeCategory.lang]?.code.toString()
  317. }
  318. }
  319. /**
  320. * 如果本地食谱存在的情况下
  321. * 下载在线食谱,需要删除本地食谱
  322. * **/
  323. contentData.devRecipes.forEach {
  324. var number = it.number ?: ""
  325. var lang = it.lang ?:"EN"
  326. deleteNumAndLangRecipe(number,lang)
  327. }
  328. //删除步骤 和其他一些
  329. recipeNumber?.let {
  330. deleteDevRecipeAccessorys(it)
  331. deleteDevRecipeCookingSteps(it)
  332. deleteDevRecipeFoods(it)
  333. deleteDevRecipeNutritions(it)
  334. deleteDevRecipeRelTags(it)
  335. deleteDevRecipePortionSizes(it)
  336. }
  337. // deleteRecipe(recipeNumber)
  338. insertDevAccessorys(contentData.devAccessorys)
  339. insertHotTags(contentData.devHotTags)
  340. insertDevPortraits(contentData.devPortraits)
  341. insertDevRecipeAccessorys(contentData.devRecipeAccessorys)
  342. insertDevRecipeCategorys(contentData.devRecipeCategorys)
  343. insertDevRecipeCookingSteps(contentData.devRecipeCookingSteps)
  344. insertDevRecipeFoods(contentData.devRecipeFoods)
  345. insertDevRecipeNutritions(contentData.devRecipeNutritions)
  346. insertDevRecipePortionSizes(contentData.devRecipePortionSizes)
  347. insertDevRecipeRelTags(contentData.devRecipeRelTags)
  348. insertDevRecipeTags(contentData.devRecipeTags)
  349. insertDevRecipes(contentData.devRecipes)
  350. }
  351. ThreadUtils.runOnMainThread {
  352. downloadResource.value = Resource(100, Resource.Status.SUCCESS)
  353. }
  354. }
  355. FoodDataProvider.getUserDatabase().runInTransaction {
  356. contentData.devRecipes.getOrNull(0)?.also {
  357. FoodDataProvider.getUserDatabase().userInfoDao().insertOnlineRecipe(
  358. UserOnLineRecipes(CURRENT_USER_ID, it.number ?: "")
  359. )
  360. }
  361. }
  362. } catch (e: Exception) {
  363. e.printStackTrace()
  364. ThreadUtils.runOnMainThread {
  365. downloadResource.value = Resource(0, Resource.Status.FAILURE)
  366. }
  367. }
  368. }
  369. companion object {
  370. const val ERR_NO_RECIPE_MATCH = -1
  371. }
  372. }