Bladeren bron

feat 订单列表

RobinTan1024 4 jaren geleden
bovenliggende
commit
17c62cc758
34 gewijzigde bestanden met toevoegingen van 633 en 171 verwijderingen
  1. 3 3
      app/src/main/java/com/doverfuelingsolutions/issp/api/CloudApi.kt
  2. 0 1
      app/src/main/java/com/doverfuelingsolutions/issp/api/FuelInfoApi.kt
  3. 1 1
      app/src/main/java/com/doverfuelingsolutions/issp/api/dto/DFSResult.kt
  4. 5 2
      app/src/main/java/com/doverfuelingsolutions/issp/data/GlobalData.kt
  5. 16 10
      app/src/main/java/com/doverfuelingsolutions/issp/data/GlobalDataProvider.kt
  6. 57 3
      app/src/main/java/com/doverfuelingsolutions/issp/fusion/FusionManager.kt
  7. 1 1
      app/src/main/java/com/doverfuelingsolutions/issp/utils/StringUtil.kt
  8. 17 0
      app/src/main/java/com/doverfuelingsolutions/issp/utils/ThreadUtil.kt
  9. 2 0
      app/src/main/java/com/doverfuelingsolutions/issp/utils/log/DFSLog.kt
  10. 11 10
      app/src/main/java/com/doverfuelingsolutions/issp/utils/log/DFSLogger.kt
  11. 1 0
      app/src/main/java/com/doverfuelingsolutions/issp/utils/sp/SPKeys.kt
  12. 12 0
      app/src/main/java/com/doverfuelingsolutions/issp/utils/thread/DFSThreadFactory.kt
  13. 36 30
      app/src/main/java/com/doverfuelingsolutions/issp/view/MainActivity.kt
  14. 27 0
      app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/ChooseListAdapter.kt
  15. 0 27
      app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/NozzleChooseAdapter.kt
  16. 30 0
      app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/OrderListAdapter.kt
  17. 31 0
      app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentBasic.kt
  18. 21 0
      app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentConstants.kt
  19. 26 25
      app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentNozzle.kt
  20. 109 0
      app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentOrderList.kt
  21. 19 21
      app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentSelect.kt
  22. 20 29
      app/src/main/java/com/doverfuelingsolutions/issp/view/widget/FragmentToolbar.kt
  23. 7 0
      app/src/main/res/drawable/order_item.xml
  24. 2 1
      app/src/main/res/layout/adapter_choose_item.xml
  25. 77 0
      app/src/main/res/layout/adapter_order_item.xml
  26. 26 0
      app/src/main/res/layout/fragment_basic.xml
  27. 3 4
      app/src/main/res/layout/fragment_nozzle.xml
  28. 45 0
      app/src/main/res/layout/fragment_nozzle_orders.xml
  29. 1 0
      app/src/main/res/values/colors.xml
  30. 1 0
      app/src/main/res/values/keys.xml
  31. 14 2
      app/src/main/res/values/strings.xml
  32. 10 0
      app/src/main/res/values/styles.xml
  33. 0 0
      md
  34. 2 1
      waynelib_/src/main/java/com/wayne/www/waynelib/fdc/log_print/MyLoggerHs.java

+ 3 - 3
app/src/main/java/com/doverfuelingsolutions/issp/api/CloudApi.kt

@@ -11,6 +11,7 @@ import com.doverfuelingsolutions.issp.api.service.ServiceLogin
 import com.doverfuelingsolutions.issp.api.service.ServiceTrx
 import com.doverfuelingsolutions.issp.data.GlobalData
 import com.doverfuelingsolutions.issp.utils.StringUtil
+import com.doverfuelingsolutions.issp.utils.ThreadUtil
 import com.doverfuelingsolutions.issp.utils.log.DFSLog
 import com.wayne.www.waynelib.webservice.entity.ClientPosTrx
 import com.wayne.www.waynelib.webservice.entity.PosTrxType
@@ -75,7 +76,7 @@ object CloudApi {
                     GlobalData.accountName.set(accountName)
                     GlobalData.password.set(password)
                     GlobalData.accessToken.set(body.access_token)
-                    GlobalData.accessTokenExpire.set((System.currentTimeMillis() + body.expires_in.toLong() * 1000).toString())
+                    GlobalData.accessTokenExpire.set((System.currentTimeMillis() + body.expires_in.toLong() * 1000))
                     // [{\"Id\":\"dfc5cc2d-a5ef-4bc4-b1b0-4aa0c82a73de\",\"Name\":\"GlobalUnit\"}]
                     if (body.BusinessUnits.isNotBlank()) {
                         val jsonArray = JSONArray(body.BusinessUnits)
@@ -170,8 +171,7 @@ object CloudApi {
                 if (code != 200 || body.isNullOrEmpty() || body[0].value.isBlank()) {
                     it.resume(DFSResult.fail(R.string.return_data_error))
                 } else {
-                    // TODO 需要优化
-                    GlobalScope.launch(Dispatchers.IO) {
+                    ThreadUtil.io {
                         try {
                             val file = File(DFSApplication.instance.applicationContext.filesDir, body[0].name)
                             if (file.isFile) file.delete()

+ 0 - 1
app/src/main/java/com/doverfuelingsolutions/issp/api/FuelInfoApi.kt

@@ -98,7 +98,6 @@ object FuelInfoApi {
         val ip = SPUtil.getString(SPKeys.FUEL_IP)
         val port = SPUtil.getString(SPKeys.FUEL_PORT)
         val url = "http://$ip:$port"
-        DFSLog.i("new url is $url")
         return RetrofitUtil.getGsonBuilder().baseUrl(url).build().create(ServiceStation::class.java)
     }
 

+ 1 - 1
app/src/main/java/com/doverfuelingsolutions/issp/api/dto/DFSResult.kt

@@ -5,7 +5,7 @@ import com.doverfuelingsolutions.issp.R
 import com.doverfuelingsolutions.issp.utils.StringUtil
 import java.util.*
 
-data class DFSResult<T> constructor(
+class DFSResult<T> private constructor(
     val success: Boolean = true,
     val data: T?,
     val message: String = "success"

+ 5 - 2
app/src/main/java/com/doverfuelingsolutions/issp/data/GlobalData.kt

@@ -3,6 +3,7 @@ package com.doverfuelingsolutions.issp.data
 import com.doverfuelingsolutions.issp.utils.DeviceUtil
 import com.doverfuelingsolutions.issp.utils.sp.SPKeys
 import com.doverfuelingsolutions.issp.utils.sp.SPUtil
+import com.wayne.www.waynelib.fdc.message.DeviceClass
 
 object GlobalData {
 
@@ -12,14 +13,16 @@ object GlobalData {
     val accountName = GlobalDataProvider("", SPKeys.ACCOUNT_NAME)
     val password = GlobalDataProvider("", SPKeys.PASSWORD)
     val accessToken = GlobalDataProvider("", SPKeys.ACCESS_TOKEN)
-    val accessTokenExpire = GlobalDataProvider("0", SPKeys.ACCESS_TOKEN_EXPIRE)
+    val accessTokenExpire = GlobalDataProvider(0L, SPKeys.ACCESS_TOKEN_EXPIRE)
 
     val businessId = GlobalDataProvider("", SPKeys.BU_ID)
     val businessName = GlobalDataProvider("", SPKeys.BU_NAME)
     val stationTel = GlobalDataProvider("", SPKeys.STATION_TEL)
     val stationLogoFileName = GlobalDataProvider("", SPKeys.STATION_LOGO_FILE)
 
+    val orderList = GlobalDataProvider(mutableListOf<DeviceClass>(), SPKeys.ORDER_UNSETTLED)
+
     fun init() {
-        isLogin = System.currentTimeMillis() < accessTokenExpire.get().toLong()
+        isLogin = System.currentTimeMillis() < accessTokenExpire.get()
     }
 }

+ 16 - 10
app/src/main/java/com/doverfuelingsolutions/issp/data/GlobalDataProvider.kt

@@ -1,24 +1,30 @@
 package com.doverfuelingsolutions.issp.data
 
 import com.doverfuelingsolutions.issp.utils.sp.SPUtil
+import com.google.gson.Gson
 
-class GlobalDataProvider(defaultValue: String, private val sp: String = "") {
+class GlobalDataProvider<T>(private var value: T, private val sp: String = "") {
 
-    private var value = defaultValue
-    private var isInit = false
+    companion object {
+        private val gson = Gson()
+    }
+
+    private val cls = value!!::class.java
+    private val stringDefaultValue = gson.toJson(value, cls)
 
-    fun get(): String {
-        if (sp.isNotEmpty() && !isInit) {
-            isInit = true
-            value = SPUtil.getString(sp, value)
+    init {
+        if (sp.isNotEmpty()) {
+            val spValue = SPUtil.getString(sp, stringDefaultValue)
+            if (!spValue.isBlank()) value = gson.fromJson(spValue, cls)
         }
-        return value
     }
 
-    fun set(v: String) {
+    fun get() = value
+
+    fun set(v: T) {
         value = v
         if (sp.isNotEmpty()) {
-            SPUtil.putString(sp, v)
+            SPUtil.putString(sp, gson.toJson(v, cls))
         }
     }
 }

+ 57 - 3
app/src/main/java/com/doverfuelingsolutions/issp/fusion/FusionManager.kt

@@ -5,10 +5,12 @@ import androidx.lifecycle.LifecycleObserver
 import androidx.lifecycle.OnLifecycleEvent
 import com.doverfuelingsolutions.issp.R
 import com.doverfuelingsolutions.issp.api.FuelInfoApi
+import com.doverfuelingsolutions.issp.api.dto.DFSResult
 import com.doverfuelingsolutions.issp.api.entity.NozzleInfo
 import com.doverfuelingsolutions.issp.data.GlobalData
 import com.doverfuelingsolutions.issp.fusion.callback.OnFusionEvent
 import com.doverfuelingsolutions.issp.utils.StringUtil
+import com.doverfuelingsolutions.issp.utils.ThreadUtil
 import com.doverfuelingsolutions.issp.utils.log.DFSLog
 import com.doverfuelingsolutions.issp.utils.sp.SPKeys
 import com.doverfuelingsolutions.issp.utils.sp.SPUtil
@@ -16,9 +18,7 @@ import com.wayne.www.waynelib.fdc.FdcClient
 import com.wayne.www.waynelib.fdc.OnFdcClientStateChangedListener
 import com.wayne.www.waynelib.fdc.OnFdcMessageReceivedListener
 import com.wayne.www.waynelib.fdc.OnFdcServiceResponseReceivedListener
-import com.wayne.www.waynelib.fdc.message.FdcMessage
-import com.wayne.www.waynelib.fdc.message.ServiceResponse
-import com.wayne.www.waynelib.fdc.message.ServiceResponseLogOn
+import com.wayne.www.waynelib.fdc.message.*
 import kotlinx.coroutines.*
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
@@ -135,6 +135,60 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
         }
     }
 
+    /**
+     * 获取所有未结算加油订单
+     * @param pumpId -1表示全部FP,其他代表某个FP
+     */
+    suspend fun getAllUnsettledOrder(pumpId: Int = -1) = suspendCoroutine<DFSResult<List<DeviceClass>>> {
+        ThreadUtil.io {
+            FdcClient.getDefault().sendGetAvailableFuelSaleTrxs(pumpId, { _, response ->
+                coroutineIO.launch {
+                    if (response == null || response !is ServiceResponseGetAvailableFuelSaleTrxs || response.singleFdcData == null || response.singleDeviceClass == null) {
+                        it.resume(DFSResult.fail(R.string.fail_get_order))
+                    } else {
+                        val detailList = response.singleFdcData.deviceClasses.map { dc ->
+                            DFSLog.v(dc)
+                            val asyncResultDetail = async { getOrderDetail(dc) }
+                            val result = asyncResultDetail.await()
+                            if (result.success) {
+                                DFSLog.d(result.data)
+                                result.data?.productName = nozzleList.find { it.pumpId == dc.pumpNo }?.barcodeName ?: ""
+                            }
+                            result.data
+                        }
+                        val orderList = detailList.filterNotNull()
+                        it.resume(DFSResult.success(orderList))
+                    }
+                }
+            }, 4000)
+        }
+    }
+
+    /**
+     * 挂起函数:获取订单详情
+     */
+    private suspend fun getOrderDetail(deviceClass: DeviceClass) = suspendCoroutine<DFSResult<DeviceClass>> {
+        getOrderDetailAsync(deviceClass) { asyncResult ->
+            it.resume(asyncResult)
+        }
+    }
+
+    /**
+     * 获取订单详情
+     */
+    private fun getOrderDetailAsync(deviceClass: DeviceClass, callback: (result: DFSResult<DeviceClass>) -> Unit) {
+        ThreadUtil.io {
+            FdcClient.getDefault().sendGetFuelSalesTrxDetailsRequestAsync(deviceClass.pumpNo, deviceClass.transactionSeqNo, deviceClass.releaseTokenAttribute, { _, response ->
+                if (response == null || response !is ServiceResponseGetFuelSalesTrxDetails || response.fdcData.isEmpty() || response.fdcData[0].deviceClasses.isEmpty()) {
+                    callback(DFSResult.fail(R.string.fail_get_order_detail))
+                } else {
+                    val dc = response.fdcData[0].deviceClasses[0]
+                    callback(DFSResult.success(dc))
+                }
+            }, 3000)
+        }
+    }
+
     suspend fun requestNozzleInfo() = FuelInfoApi.getNozzleInfo()
 
     private suspend fun login() = suspendCoroutine<Boolean> {

+ 1 - 1
app/src/main/java/com/doverfuelingsolutions/issp/utils/StringUtil.kt

@@ -11,7 +11,7 @@ class StringUtil {
 
         var ctxBeforeApplication: WeakReference<Context>? = null
 
-        fun get(@StringRes id: Int, vararg params: String): String = ctx.getString(id, *params)
+        fun get(@StringRes id: Int, vararg params: Any): String = ctx.getString(id, *params)
 
         private val ctx: Context get() = ctxBeforeApplication?.get() ?: DFSApplication.instance.applicationContext
     }

+ 17 - 0
app/src/main/java/com/doverfuelingsolutions/issp/utils/ThreadUtil.kt

@@ -0,0 +1,17 @@
+package com.doverfuelingsolutions.issp.utils
+
+import com.doverfuelingsolutions.issp.utils.thread.DFSThreadFactory
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
+
+object ThreadUtil {
+
+    private val logExecutor =
+        ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, LinkedBlockingQueue(100), DFSThreadFactory("log"))
+    private val ioExecutor =
+        ThreadPoolExecutor(3, 5, 0, TimeUnit.MILLISECONDS, LinkedBlockingQueue(20), DFSThreadFactory("io"))
+
+    fun log(runnable: Runnable) = logExecutor.execute(runnable)
+    fun io(runnable: Runnable) = ioExecutor.execute(runnable)
+}

+ 2 - 0
app/src/main/java/com/doverfuelingsolutions/issp/utils/log/DFSLog.kt

@@ -9,6 +9,8 @@ class DFSLog {
         const val TAG = "robin"
         var console = true
         var file = true
+        var saveDays = 30
+
         @Suppress("DEPRECATION")
         var folder = "${Environment.getExternalStorageDirectory().absolutePath}/dfs/log"
 

+ 11 - 10
app/src/main/java/com/doverfuelingsolutions/issp/utils/log/DFSLogger.kt

@@ -2,22 +2,19 @@ package com.doverfuelingsolutions.issp.utils.log
 
 import android.os.Build
 import android.util.Log
+import com.doverfuelingsolutions.issp.utils.ThreadUtil
+import com.google.gson.Gson
 import java.io.File
 import java.io.PrintWriter
 import java.io.StringWriter
 import java.text.SimpleDateFormat
 import java.util.*
-import java.util.concurrent.LinkedBlockingQueue
-import java.util.concurrent.ThreadPoolExecutor
-import java.util.concurrent.TimeUnit
 
 object DFSLogger {
 
     private val LINE_BR = System.getProperty("line.separator")?.toString() ?: "\n"
-
     private val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.CHINA)
-
-    private val threadPoolExecutor = ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, LinkedBlockingQueue(50))
+    private val gson = Gson()
 
     fun log(type: DFSLogType, vararg contents: Any?) {
         val string = when (contents.size) {
@@ -34,9 +31,9 @@ object DFSLogger {
     }
 
     private fun file(type: DFSLogType, content: String) {
-        threadPoolExecutor.execute {
-            Thread.currentThread().name = "DFSLogger-${ UUID.randomUUID().toString().slice(0..4) }"
-            val format = formatter.format(Date())
+        ThreadUtil.log {
+            val today = Date()
+            val format = formatter.format(today)
             val date = format.substring(0..9)
             val time = format.substring(11)
             val folder = File(DFSLog.folder)
@@ -52,6 +49,10 @@ object DFSLogger {
                             "Android SDK: ${Build.VERSION.SDK_INT}$LINE_BR" +
                             "************* Log Info ****************$LINE_BR$LINE_BR"
                 )
+                // delete old file
+                val dateOld = formatter.format(Date(today.time - 86400000L * DFSLog.saveDays)).substring(0..9)
+                val oldFile = File("${DFSLog.folder}/${dateOld}")
+                oldFile.deleteOnExit()
             }
             mFile.appendText("$time [${type.value}] $content$LINE_BR")
         }
@@ -70,7 +71,7 @@ object DFSLogger {
     private fun stringify(content: Any?): String = when (content) {
         is Iterable<*> -> content.joinToString { it.toString() }
         is Throwable -> getFullStackTrace(content)
-        else -> content.toString()
+        else -> gson.toJson(content)
     }
 
     private fun getFullStackTrace(throwable: Throwable): String {

+ 1 - 0
app/src/main/java/com/doverfuelingsolutions/issp/utils/sp/SPKeys.kt

@@ -32,5 +32,6 @@ class SPKeys {
         val FUEL_PORT: String = StringUtil.get(R.string.sp_fuel_port)
 
         val FUEL_BARCODE: String = StringUtil.get(R.string.sp_fuel_barcode)
+        val ORDER_UNSETTLED: String = StringUtil.get(R.string.sp_order_unsettled)
     }
 }

+ 12 - 0
app/src/main/java/com/doverfuelingsolutions/issp/utils/thread/DFSThreadFactory.kt

@@ -0,0 +1,12 @@
+package com.doverfuelingsolutions.issp.utils.thread
+
+import java.util.concurrent.ThreadFactory
+
+class DFSThreadFactory(private val basicName: String) : ThreadFactory {
+
+    companion object {
+        private var count = 0
+    }
+
+    override fun newThread(r: Runnable): Thread = Thread(r, "dfs.$basicName.${count++}")
+}

+ 36 - 30
app/src/main/java/com/doverfuelingsolutions/issp/view/MainActivity.kt

@@ -15,6 +15,7 @@ import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.lifecycleScope
 import com.doverfuelingsolutions.issp.R
+import com.doverfuelingsolutions.issp.api.FuelInfoApi
 import com.doverfuelingsolutions.issp.data.GlobalData
 import com.doverfuelingsolutions.issp.databinding.ActivityMainBinding
 import com.doverfuelingsolutions.issp.fusion.FusionError
@@ -22,9 +23,7 @@ import com.doverfuelingsolutions.issp.fusion.FusionManager
 import com.doverfuelingsolutions.issp.fusion.callback.OnFusionEvent
 import com.doverfuelingsolutions.issp.utils.ActivityUtil
 import com.doverfuelingsolutions.issp.utils.StringUtil
-import com.doverfuelingsolutions.issp.view.fragment.FragmentHolder
-import com.doverfuelingsolutions.issp.view.fragment.FragmentNozzle
-import com.doverfuelingsolutions.issp.view.fragment.FragmentSelect
+import com.doverfuelingsolutions.issp.view.fragment.*
 import com.wayne.www.waynelib.fdc.FdcClient
 import com.youth.banner.adapter.BannerImageAdapter
 import com.youth.banner.holder.BannerImageHolder
@@ -40,9 +39,6 @@ class MainActivity : AppCompatActivity(),
         fun start(context: Context) {
             Intent(context, MainActivity::class.java).let { context.startActivity(it) }
         }
-
-        const val keyOnBack = "back"
-        const val keyOnHome = "home"
     }
 
     private val binding: ActivityMainBinding by lazy { DataBindingUtil.setContentView(this, R.layout.activity_main) }
@@ -50,7 +46,8 @@ class MainActivity : AppCompatActivity(),
 
     private val fragmentHolder = FragmentHolder()
     private val fragmentSelect = FragmentSelect()
-    private val fragmentNozzle = FragmentNozzle()
+    private val fragmentNozzle: FragmentNozzle by lazyOf(FragmentNozzle())
+    private val fragmentOrderList: FragmentOrderList by lazyOf(FragmentOrderList())
     private var isFragmentHoldBack = true
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -102,19 +99,31 @@ class MainActivity : AppCompatActivity(),
     }
 
     override fun onFragmentResult(requestKey: String, result: Bundle) {
-        when (requestKey) {
-            keyOnBack -> {
+        if (requestKey != FragmentConstants.fragmentEvent) return
+
+        when (result.getString(FragmentConstants.eventType)) {
+            FragmentConstants.eventHome -> {
+                if (supportFragmentManager.backStackEntryCount > 0)
+                    supportFragmentManager.popBackStack(supportFragmentManager.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+            }
+            FragmentConstants.eventBack -> {
                 supportFragmentManager.popBackStack()
             }
-            keyOnHome -> {
-                if (supportFragmentManager.backStackEntryCount > 0) {
-                    supportFragmentManager.popBackStack(supportFragmentManager.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+            FragmentConstants.eventSearchBy -> {
+                val type = result.getString(FragmentConstants.searchType)
+                if (type == FragmentConstants.typeNozzle) {
+                    setFragment(fragmentNozzle, true)
+                } else if (type == FragmentConstants.typeOilName) {
+                    setFragment(fragmentNozzle, true)
                 }
             }
-            FragmentSelect.keyOnSelect -> {
-                val isNozzle = result.getBoolean(FragmentSelect.keyIsNozzle)
-                // TODO 油品和枪号选择界面
-                setFragment(if (isNozzle) fragmentNozzle else fragmentHolder, true)
+            FragmentConstants.eventOrderList -> {
+                val type = result.getString(FragmentConstants.searchType)
+                if (type == FragmentConstants.typeNozzle) {
+                    fragmentOrderList.viewModel.type.value = FragmentConstants.typeNozzle
+                    fragmentOrderList.viewModel.nozzle.value = result.getInt(FragmentConstants.pumpId)
+                    setFragment(fragmentOrderList, true)
+                }
             }
         }
     }
@@ -135,6 +144,14 @@ class MainActivity : AppCompatActivity(),
             when (code) {
                 FusionError.Success -> {
                     setFragment(fragmentSelect)
+                    // Preload order data after first connect
+                    lifecycleScope.launch {
+                        FusionManager.getAllUnsettledOrder(-1).run {
+                            if (success && data != null) {
+                                GlobalData.orderList.set(data.toMutableList())
+                            }
+                        }
+                    }
                 }
                 FusionError.WrongAddress, FusionError.Timeout, FusionError.GetNozzleInfo -> {
                     setFragment(fragmentHolder)
@@ -181,9 +198,7 @@ class MainActivity : AppCompatActivity(),
         }
         binding.clock.setOnLongClickListener(this)
 
-        supportFragmentManager.setFragmentResultListener(FragmentSelect.keyOnSelect, this, this)
-        supportFragmentManager.setFragmentResultListener(keyOnBack, this, this)
-        supportFragmentManager.setFragmentResultListener(keyOnHome, this, this)
+        supportFragmentManager.setFragmentResultListener(FragmentConstants.fragmentEvent, this, this)
         setFragment(fragmentHolder)
     }
 
@@ -194,19 +209,10 @@ class MainActivity : AppCompatActivity(),
 
     private fun setFragment(fragment: Fragment, back: Boolean = false) {
         val contain = supportFragmentManager.fragments.contains(fragment)
-        val show = !fragment.isHidden
-
-        if (contain && show) return
+        if (contain) return
 
         supportFragmentManager.beginTransaction().run {
-            supportFragmentManager.fragments.forEach {
-                if (fragment != it && !it.isHidden) hide(it)
-            }
-            if (!contain) {
-                add(R.id.fragmentBox, fragment)
-            } else if (fragment.isHidden) {
-                show(fragment)
-            }
+            replace(R.id.fragmentBox, fragment)
             if (back) {
                 addToBackStack(fragment.javaClass.simpleName)
             }

+ 27 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/ChooseListAdapter.kt

@@ -0,0 +1,27 @@
+package com.doverfuelingsolutions.issp.view.adapter
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.doverfuelingsolutions.issp.R
+import kotlinx.android.synthetic.main.adapter_choose_item.view.*
+
+class ChooseListAdapter<T>(private val chooseList: List<T>, private val handler: (item: T) -> Unit) :
+    RecyclerView.Adapter<ChooseListAdapter.ChooseViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChooseViewHolder {
+        val view = View.inflate(parent.context, R.layout.adapter_choose_item, null)
+        return ChooseViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: ChooseViewHolder, position: Int) {
+        holder.itemView.chooseItemView.text = chooseList[position].toString()
+        holder.itemView.chooseItemView.setOnClickListener {
+            handler.invoke(chooseList[position])
+        }
+    }
+
+    override fun getItemCount(): Int = chooseList.size
+
+    class ChooseViewHolder(view: View) : RecyclerView.ViewHolder(view)
+}

+ 0 - 27
app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/NozzleChooseAdapter.kt

@@ -1,27 +0,0 @@
-package com.doverfuelingsolutions.issp.view.adapter
-
-import android.view.View
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-import com.doverfuelingsolutions.issp.R
-import kotlinx.android.synthetic.main.adapter_choose_nozzle.view.*
-
-class NozzleChooseAdapter(private val nozzleList: List<Int>, private val handler: (nozzle: Int) -> Unit) :
-    RecyclerView.Adapter<NozzleChooseAdapter.NozzleChooseViewHolder>() {
-
-    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NozzleChooseViewHolder {
-        val view = View.inflate(parent.context, R.layout.adapter_choose_nozzle, null)
-        return NozzleChooseViewHolder(view)
-    }
-
-    override fun onBindViewHolder(holder: NozzleChooseViewHolder, position: Int) {
-        holder.itemView.chooseNozzleNum.text = nozzleList[position].toString()
-        holder.itemView.chooseNozzleNum.setOnClickListener {
-            handler.invoke(nozzleList[position])
-        }
-    }
-
-    override fun getItemCount(): Int = nozzleList.size
-
-    class NozzleChooseViewHolder(view: View) : RecyclerView.ViewHolder(view)
-}

+ 30 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/OrderListAdapter.kt

@@ -0,0 +1,30 @@
+package com.doverfuelingsolutions.issp.view.adapter
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.doverfuelingsolutions.issp.R
+import com.doverfuelingsolutions.issp.utils.StringUtil
+import com.wayne.www.waynelib.fdc.message.DeviceClass
+import kotlinx.android.synthetic.main.adapter_order_item.view.*
+
+class OrderListAdapter(private val orderList: List<DeviceClass>, private val handler: () -> Unit) : RecyclerView.Adapter<OrderListAdapter.OrderListViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrderListViewHolder {
+        val view = View.inflate(parent.context, R.layout.adapter_order_item, null)
+        return OrderListViewHolder(view)
+    }
+
+    override fun onBindViewHolder(holder: OrderListViewHolder, position: Int) {
+        val order = orderList[position]
+        holder.itemView.oilName.text = order.productName
+        holder.itemView.nozzleName.text = StringUtil.get(R.string.nozzle_name, order.nozzleNo)
+        holder.itemView.fuelTime.text = order.endTimeStamp.substring(10)
+        holder.itemView.amount.text = StringUtil.get(R.string.amount_rmb, order.amount.toString())
+        holder.itemView.volume.text = StringUtil.get(R.string.volume_l, order.volume.toString())
+    }
+
+    override fun getItemCount() = orderList.size
+
+    class OrderListViewHolder(view: View) : RecyclerView.ViewHolder(view)
+}

+ 31 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentBasic.kt

@@ -0,0 +1,31 @@
+package com.doverfuelingsolutions.issp.view.fragment
+
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.setFragmentResult
+import com.doverfuelingsolutions.issp.utils.log.DFSLog
+import com.doverfuelingsolutions.issp.view.MainActivity
+import com.doverfuelingsolutions.issp.view.widget.FragmentToolbar
+
+abstract class FragmentBasic : Fragment() {
+
+    fun setToolBar(toolbar: FragmentToolbar) {
+        toolbar.setBackHandler { back() }
+        toolbar.setOnCountdownFinish { home() }
+        toolbar.setHomeHandler { home() }
+    }
+
+    private fun back() {
+        setFragmentResult(
+            FragmentConstants.fragmentEvent,
+            bundleOf(Pair(FragmentConstants.eventType, FragmentConstants.eventBack))
+        )
+    }
+
+    private fun home() {
+        setFragmentResult(
+            FragmentConstants.fragmentEvent,
+            bundleOf(Pair(FragmentConstants.eventType, FragmentConstants.eventHome))
+        )
+    }
+}

+ 21 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentConstants.kt

@@ -0,0 +1,21 @@
+package com.doverfuelingsolutions.issp.view.fragment
+
+class FragmentConstants {
+
+    companion object {
+
+        /* fragment communication */
+        const val fragmentEvent = "fragmentEvent"
+        const val eventType = "eventType"
+        const val eventBack = "back"
+        const val eventHome = "home"
+        const val eventSearchBy = "searchBy"
+        const val eventOrderList = "orderList"
+
+        const val searchType = "searchType"
+        const val pumpId = "pumpId"
+
+        const val typeNozzle = "nozzle"
+        const val typeOilName = "oilName"
+    }
+}

+ 26 - 25
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentNozzle.kt

@@ -6,51 +6,46 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.core.os.bundleOf
 import androidx.databinding.DataBindingUtil
-import androidx.fragment.app.Fragment
 import androidx.fragment.app.setFragmentResult
-import androidx.lifecycle.ViewModel
 import androidx.recyclerview.widget.GridLayoutManager
 import com.doverfuelingsolutions.issp.R
 import com.doverfuelingsolutions.issp.databinding.FragmentNozzleBinding
-import com.doverfuelingsolutions.issp.fusion.FusionError
 import com.doverfuelingsolutions.issp.fusion.FusionManager
-import com.doverfuelingsolutions.issp.utils.StringUtil
 import com.doverfuelingsolutions.issp.utils.log.DFSLog
-import com.doverfuelingsolutions.issp.view.MainActivity
-import com.doverfuelingsolutions.issp.view.adapter.NozzleChooseAdapter
+import com.doverfuelingsolutions.issp.view.adapter.ChooseListAdapter
 import com.scwang.smart.refresh.header.ClassicsHeader
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
 
-class FragmentNozzle : Fragment() {
+class FragmentNozzle : FragmentBasic() {
 
     private lateinit var binding: FragmentNozzleBinding
-    private val nozzleAdapter: NozzleChooseAdapter by lazy {
-        NozzleChooseAdapter(FusionManager.nozzleList.map { it.physicalId }, this::select)
+
+    private val nozzleList: List<Int> by lazy { FusionManager.nozzleList.map { it.physicalId } }
+    private val chooseListAdapter: ChooseListAdapter<Int> by lazy {
+        ChooseListAdapter(
+            nozzleList,
+            this::select
+        )
     }
 
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
         binding = DataBindingUtil.inflate(inflater, R.layout.fragment_nozzle, container, false)
-        binding.fragmentToolbar.setBackHandler { back() }
-        binding.fragmentToolbar.setOnCountdownFinish { back() }
-        binding.fragmentToolbar.setHomeHandler { home() }
 
         binding.nozzleList.layoutManager = GridLayoutManager(context, 4)
-        binding.nozzleList.adapter = nozzleAdapter
+        binding.nozzleList.adapter = chooseListAdapter
 
         binding.smartRefreshLayout.setEnableLoadMore(false)
         binding.smartRefreshLayout.setRefreshHeader(ClassicsHeader(context))
         binding.smartRefreshLayout.setOnRefreshListener { handleNozzleRefresh() }
-        return binding.root
-    }
 
-    private fun back() {
-        setFragmentResult(MainActivity.keyOnBack, bundleOf())
-    }
-
-    private fun home() {
-        setFragmentResult(MainActivity.keyOnHome, bundleOf())
+        setToolBar(binding.fragmentToolbar)
+        return binding.root
     }
 
     private fun handleNozzleRefresh() {
@@ -68,8 +63,14 @@ class FragmentNozzle : Fragment() {
     }
 
     private fun select(nozzle: Int) {
-        DFSLog.i("nozzle: $nozzle")
+        // TODO 究竟是传 FP 还是物理枪号?
+        setFragmentResult(
+            FragmentConstants.fragmentEvent,
+            bundleOf(
+                Pair(FragmentConstants.eventType, FragmentConstants.eventOrderList),
+                Pair(FragmentConstants.searchType, FragmentConstants.typeNozzle),
+                Pair(FragmentConstants.pumpId, nozzle),
+            )
+        )
     }
-
-    class FragmentNozzleViewModel : ViewModel() {}
 }

+ 109 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentOrderList.kt

@@ -0,0 +1,109 @@
+package com.doverfuelingsolutions.issp.view.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.GridLayoutManager
+import com.doverfuelingsolutions.issp.R
+import com.doverfuelingsolutions.issp.data.GlobalData
+import com.doverfuelingsolutions.issp.databinding.FragmentNozzleOrdersBinding
+import com.doverfuelingsolutions.issp.fusion.FusionManager
+import com.doverfuelingsolutions.issp.utils.log.DFSLog
+import com.doverfuelingsolutions.issp.view.adapter.OrderListAdapter
+import com.google.android.material.snackbar.Snackbar
+import com.google.gson.Gson
+import com.scwang.smart.refresh.header.ClassicsHeader
+import com.wayne.www.waynelib.fdc.message.DeviceClass
+import kotlinx.coroutines.launch
+
+class FragmentOrderList : FragmentBasic() {
+
+    private lateinit var binding: FragmentNozzleOrdersBinding
+    private val orderList = arrayListOf<DeviceClass>()
+    private val orderListAdapter: OrderListAdapter by lazyOf(OrderListAdapter(orderList, this::select))
+
+    private val snackbar: Snackbar by lazy {
+        Snackbar.make(binding.smartRefreshLayout, "", 4000)
+            .apply { anchorView = binding.smartRefreshLayout }
+    }
+    val viewModel = FragmentNozzleViewModel()
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        // TODO 其他 Fragment 也需要避免重复装载
+        if (!this::binding.isInitialized) {
+            binding = DataBindingUtil.inflate(inflater, R.layout.fragment_nozzle_orders, container, false)
+
+            binding.orderList.layoutManager = GridLayoutManager(context, 2)
+            binding.orderList.adapter = orderListAdapter
+
+            binding.smartRefreshLayout.setEnableLoadMore(false)
+            binding.smartRefreshLayout.setRefreshHeader(ClassicsHeader(context))
+            binding.smartRefreshLayout.setOnRefreshListener { handleOrdersRefresh() }
+
+            binding.lifecycleOwner = this
+            binding.viewModel = viewModel
+
+            setToolBar(binding.fragmentToolbar)
+        }
+        return binding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        // TODO 订单状态:锁定、清理
+        lifecycleScope.launch {
+            binding.orderList.scrollTo(0, 0)
+            // local data
+            val localList = filterByMode(GlobalData.orderList.get())
+            if (orderList.isNotEmpty()) orderList.clear()
+            orderList.addAll(localList)
+            orderListAdapter.notifyDataSetChanged()
+
+            // remote data
+            val result = FusionManager.getAllUnsettledOrder(viewModel.nozzle.value!!)
+            if (result.success && result.data != null) {
+                val localHash = localList.joinToString { it.transactionSeqNo }
+                val remoteHash = result.data.joinToString { it.transactionSeqNo }
+                if (localHash != remoteHash) {
+                    if (orderList.isNotEmpty()) orderList.clear()
+                    orderList.addAll(filterByMode(result.data))
+                    orderListAdapter.notifyDataSetChanged()
+
+                    snackbar.setText(R.string.data_refresh).show()
+                }
+            }
+        }
+    }
+
+    private fun handleOrdersRefresh() {
+    }
+
+    private fun select() {
+        DFSLog.i("select")
+    }
+
+    private fun filterByMode(orders: List<DeviceClass>): List<DeviceClass> {
+        val isNozzle = viewModel.type.value == FragmentConstants.typeNozzle
+        val physicalNozzle = viewModel.nozzle.value!!
+        return orders.filter {
+            if (isNozzle) {
+                it.nozzleNo == physicalNozzle
+            } else {
+                // TODO 油品选择
+                true
+            }
+        }
+    }
+
+    class FragmentNozzleViewModel : ViewModel() {
+        val type = MutableLiveData(FragmentConstants.typeNozzle)
+        val nozzle = MutableLiveData(0)
+        val oilName = MutableLiveData("")
+    }
+}

+ 19 - 21
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentSelect.kt

@@ -17,14 +17,7 @@ import com.doverfuelingsolutions.issp.data.GlobalData
 import com.doverfuelingsolutions.issp.databinding.FragmentSearchTypeBinding
 
 class FragmentSelect : Fragment(),
-    View.OnClickListener,
-    View.OnLongClickListener {
-
-    companion object {
-        const val keyOnSelect = "FragmentSelect"
-        const val keyIsNozzle = "isNozzle"
-        const val keyIsOil = "isOil"
-    }
+    View.OnClickListener {
 
     private val viewModel: SearchTypeViewModel by viewModels()
 
@@ -37,31 +30,36 @@ class FragmentSelect : Fragment(),
 
         binding.chooseOil.setOnClickListener(this)
         binding.chooseNozzle.setOnClickListener(this)
-        binding.versionNum.setOnLongClickListener(this)
         return binding.root
     }
 
     override fun onClick(v: View?) {
         when (v) {
-            binding.chooseNozzle -> setFragmentResult(keyOnSelect, bundleOf(Pair(keyIsNozzle, true)))
-            binding.chooseOil -> setFragmentResult(keyOnSelect, bundleOf(Pair(keyIsOil, true)))
-        }
-    }
-
-    override fun onLongClick(v: View?): Boolean {
-        when (v) {
-            binding.versionNum -> {
-            }
-            else -> return false
+            binding.chooseNozzle -> setFragmentResult(
+                FragmentConstants.fragmentEvent,
+                bundleOf(
+                    Pair(FragmentConstants.eventType, FragmentConstants.eventSearchBy),
+                    Pair(FragmentConstants.searchType, FragmentConstants.typeNozzle)
+                )
+            )
+            binding.chooseOil -> setFragmentResult(
+                FragmentConstants.fragmentEvent,
+                bundleOf(
+                    Pair(FragmentConstants.eventType, FragmentConstants.eventSearchBy),
+                    Pair(FragmentConstants.searchType, FragmentConstants.typeOilName)
+                )
+            )
         }
-        return true
     }
 
     class SearchTypeViewModel : ViewModel() {
         val deviceNum = MutableLiveData(GlobalData.serialNumber.get())
         val version: MutableLiveData<String> by lazy {
             val pm = DFSApplication.instance.applicationContext.packageManager
-            val info = pm.getPackageInfo(DFSApplication.instance.applicationContext.packageName, android.content.pm.PackageManager.GET_ACTIVITIES)
+            val info = pm.getPackageInfo(
+                DFSApplication.instance.applicationContext.packageName,
+                android.content.pm.PackageManager.GET_ACTIVITIES
+            )
             MutableLiveData(info.versionName)
         }
     }

+ 20 - 29
app/src/main/java/com/doverfuelingsolutions/issp/view/widget/FragmentToolbar.kt

@@ -5,11 +5,15 @@ import android.util.AttributeSet
 import android.view.View
 import android.widget.FrameLayout
 import com.doverfuelingsolutions.issp.R
+import com.doverfuelingsolutions.issp.utils.log.DFSLog
 import kotlinx.android.synthetic.main.widget_fragment_toolbar.view.*
 import kotlinx.coroutines.*
+import java.util.*
 
 class FragmentToolbar(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
 
+    private val id = UUID.randomUUID().toString().slice(0..1)
+
     val view: View = View.inflate(context, R.layout.widget_fragment_toolbar, this)
 
     private var isBackVisible = true
@@ -28,12 +32,9 @@ class FragmentToolbar(context: Context, attrs: AttributeSet) : FrameLayout(conte
             materialToolbar.title = value
         }
 
-    private var countdownTime: Int = 60
+    private val countdownTime: Int
     private var countdownAutoStart = true
-    private var mOnCountdownFinish: (() -> Unit)? = null
-
-    private var coroutineMain = CoroutineScope(Dispatchers.Main)
-    private var coroutineJob: Job? = null
+    private var countdownEvent: (() -> Unit)? = null
 
     private var backHandler: (() -> Unit)? = null
     private var homeHandler: (() -> Unit)? = null
@@ -55,37 +56,27 @@ class FragmentToolbar(context: Context, attrs: AttributeSet) : FrameLayout(conte
         buttonHome.setOnClickListener { homeHandler?.invoke() }
     }
 
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-
-        if (countdownAutoStart) countdown(countdownTime)
-    }
+    private lateinit var job: Job
 
-    override fun onDetachedFromWindow() {
-        super.onDetachedFromWindow()
+    override fun onWindowVisibilityChanged(visibility: Int) {
+        super.onWindowVisibilityChanged(visibility)
 
-        if (mOnCountdownFinish != null) mOnCountdownFinish = null
-        if (backHandler != null) backHandler = null
-        if (homeHandler != null) homeHandler = null
-        if (coroutineJob?.isActive == true) coroutineJob?.cancel()
-    }
-
-    private fun countdown(seconds: Int = countdownTime) {
-        if (coroutineJob?.isActive == true) {
-            coroutineJob?.cancel()
-        }
-        coroutineJob = coroutineMain.launch {
-            repeat(seconds) {
-                val num = seconds - it
-                countdownNum.text = num.toString()
-                delay(1000)
-                if (num == 1) mOnCountdownFinish?.invoke()
+        if (visibility == VISIBLE) {
+            job = GlobalScope.launch(Dispatchers.Main) {
+                repeat(countdownTime) {
+                    val num = countdownTime - it
+                    countdownNum.text = num.toString()
+                    delay(1000)
+                    if (num <= 1) countdownEvent?.invoke()
+                }
             }
+        } else if (job.isActive) {
+            job.cancel()
         }
     }
 
     fun setOnCountdownFinish(listener: () -> Unit) {
-        mOnCountdownFinish = listener
+        countdownEvent = listener
     }
 
     fun setBackHandler(handler: () -> Unit) {

+ 7 - 0
app/src/main/res/drawable/order_item.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <stroke android:width="4dp"
+        android:color="@color/colorPrimary"/>
+</shape>

+ 2 - 1
app/src/main/res/layout/adapter_choose_nozzle.xml → app/src/main/res/layout/adapter_choose_item.xml

@@ -5,13 +5,14 @@
     android:paddingHorizontal="4dp">
 
     <androidx.appcompat.widget.AppCompatTextView
-        android:id="@+id/chooseNozzleNum"
+        android:id="@+id/chooseItemView"
         android:text="@string/example_num"
         android:gravity="center"
         android:paddingTop="20dp"
         android:paddingBottom="20dp"
         android:textColor="@color/white"
         android:textSize="44sp"
+        android:textStyle="bold"
         android:background="@color/colorPrimary"
         style="@style/fullWidth" />
 </androidx.appcompat.widget.LinearLayoutCompat>

+ 77 - 0
app/src/main/res/layout/adapter_order_item.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="@style/fullWidth"
+    android:paddingStart="4dp"
+    android:paddingTop="12dp"
+    android:paddingEnd="8dp">
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        style="@style/fullWidth"
+        android:orientation="horizontal"
+        android:gravity="center"
+        android:weightSum="1"
+        android:background="@drawable/order_item">
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/oilName"
+            style="@style/wrap"
+            android:background="@color/colorPrimary"
+            android:gravity="center"
+            android:paddingHorizontal="20dp"
+            android:paddingVertical="34dp"
+            android:text="@string/example_oil"
+            android:textColor="@color/white"
+            android:textSize="42sp"
+            android:textStyle="bold" />
+
+        <androidx.appcompat.widget.LinearLayoutCompat
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:paddingStart="20dp"
+            android:paddingEnd="10dp">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/nozzleName"
+                style="@style/wrap"
+                android:layout_marginBottom="4dp"
+                android:text="@string/example_nozzle_name"
+                android:textColor="@color/color555"
+                android:textSize="32sp"
+                android:textStyle="bold" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/fuelTime"
+                style="@style/wrap"
+                android:text="@string/example_date_hms"
+                android:textColor="@color/color777"
+                android:textSize="26sp" />
+        </androidx.appcompat.widget.LinearLayoutCompat>
+
+        <androidx.appcompat.widget.LinearLayoutCompat
+            style="@style/wrap"
+            android:layout_marginEnd="20dp"
+            android:gravity="end"
+            android:orientation="vertical">
+
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/amount"
+                style="@style/wrap"
+                android:layout_marginBottom="4dp"
+                android:text="@string/example_amount"
+                android:textColor="@color/colorYellow"
+                android:textSize="32sp"
+                android:textStyle="bold" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/volume"
+                style="@style/wrap"
+                android:text="@string/example_volume"
+                android:textColor="@color/color777"
+                android:textSize="26sp" />
+        </androidx.appcompat.widget.LinearLayoutCompat>
+    </androidx.appcompat.widget.LinearLayoutCompat>
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 26 - 0
app/src/main/res/layout/fragment_basic.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="@style/match">
+
+    <com.doverfuelingsolutions.issp.view.widget.FragmentToolbar
+        android:id="@+id/fragmentToolbar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:countdownTime="60"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:title="@string/query_by_nozzle" />
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        android:id="@+id/basicView"
+        android:orientation="vertical"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/fragmentToolbar"/>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 3 - 4
app/src/main/res/layout/fragment_nozzle.xml

@@ -29,13 +29,12 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/fragmentToolbar">
 
-            <com.scwang.smart.refresh.header.ClassicsHeader
-                style="@style/fullWidth"/>
+            <com.scwang.smart.refresh.header.ClassicsHeader style="@style/fullWidth" />
 
             <androidx.recyclerview.widget.RecyclerView
-                android:paddingHorizontal="10dp"
                 android:id="@+id/nozzleList"
-                style="@style/match" />
+                style="@style/match"
+                android:paddingHorizontal="10dp" />
         </com.scwang.smart.refresh.layout.SmartRefreshLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
 </layout>

+ 45 - 0
app/src/main/res/layout/fragment_nozzle_orders.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+        <variable
+            name="viewModel"
+            type="com.doverfuelingsolutions.issp.view.fragment.FragmentOrderList.FragmentNozzleViewModel" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout style="@style/match">
+
+        <com.doverfuelingsolutions.issp.view.widget.FragmentToolbar
+            android:id="@+id/fragmentToolbar"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            app:countdownTime="60"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:title="@string/select_pay_order" />
+
+        <com.scwang.smart.refresh.layout.SmartRefreshLayout
+            android:id="@+id/smartRefreshLayout"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/fragmentToolbar">
+
+            <com.scwang.smart.refresh.header.ClassicsHeader style="@style/fullWidth" />
+
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/orderList"
+                style="@style/match"
+                android:paddingHorizontal="10dp"
+                android:paddingTop="10dp"
+                android:paddingBottom="20dp"/>
+        </com.scwang.smart.refresh.layout.SmartRefreshLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 1 - 0
app/src/main/res/values/colors.xml

@@ -12,6 +12,7 @@
     <color name="colorSecondaryNight">#FF03DAC5</color>
 
     <color name="colorLight">#E7F8FC</color>
+    <color name="colorYellow">#FF5722</color>
     <color name="colorTransparent">#00000000</color>
 
     <color name="gray">#FF888888</color>

+ 1 - 0
app/src/main/res/values/keys.xml

@@ -21,4 +21,5 @@
     <string name="sp_fuel_ip">fuel_ip</string>
     <string name="sp_fuel_port">fuel_port</string>
     <string name="sp_fuel_barcode">fuel_barcode</string>
+    <string name="sp_order_unsettled">order_unsettled</string>
 </resources>

+ 14 - 2
app/src/main/res/values/strings.xml

@@ -32,6 +32,7 @@
     <string name="fail_save_file">保存文件失败</string>
     <string name="tip_wait">请稍候</string>
     <string name="tip_opt_fail">操作失败</string>
+    <string name="data_refresh">数据已刷新</string>
 
     <string name="device_info">设备信息</string>
     <string name="device_hardware_sn">设备硬件编码</string>
@@ -71,11 +72,22 @@
     <string name="go_check_fuel">前往检查加油信息</string>
     <string name="retry_login">重试登录</string>
 
-    <string name="query_by_nozzle">根据枪号查询</string>
+    <string name="query_by_nozzle">请选择枪号查询</string>
+    <string name="select_pay_order">请选择支付加油订单</string>
+    <string name="fail_get_order">获取订单失败</string>
+    <string name="fail_get_order_detail">获取订单详情失败</string>
+    <string name="nozzle_name">%1$s号枪</string>
+    <string name="amount_rmb">¥%1$s</string>
+    <string name="volume_l">%1$sL</string>
 
     <string name="format_date">yyyy-MM-dd HH:mm:ss</string>
 
     <string name="example_format_date">2020–12–25 12:25:00</string>
+    <string name="example_date_hms">12:25:00</string>
+    <string name="example_nozzle_name">4号枪</string>
     <string name="example_company_name">托肯恒山科技有限公司</string>
-    <string name="example_num">55</string>
+    <string name="example_num">60</string>
+    <string name="example_oil">98#</string>
+    <string name="example_amount">¥1938.12</string>
+    <string name="example_volume">730.21L</string>
 </resources>

+ 10 - 0
app/src/main/res/values/styles.xml

@@ -15,4 +15,14 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
     </style>
+
+    <style name="fullHeight">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+
+    <style name="zeroWH">
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">0dp</item>
+    </style>
 </resources>

File diff suppressed because it is too large
+ 0 - 0
md


+ 2 - 1
waynelib_/src/main/java/com/wayne/www/waynelib/fdc/log_print/MyLoggerHs.java

@@ -118,7 +118,8 @@ public class MyLoggerHs {
         if (null != saveLogStrategy) {//日志存储(无论开发还是线上都存)
             saveLogStrategy.log(Log.ERROR, customTag, new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()) + " " + customTag + " - " + msg);
         }
-        Log.println(logLevel, customTag, customTag + " - " + msg);
+        // FIXME 暂时屏蔽,看着啰嗦
+        // Log.println(logLevel, customTag, customTag + " - " + msg);
     }
 
 }

Some files were not shown because too many files changed in this diff