Bladeren bron

feat 选择枪号界面

RobinTan1024 4 jaren geleden
bovenliggende
commit
10f73af0aa

+ 5 - 1
app/build.gradle

@@ -43,7 +43,7 @@ dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     implementation 'androidx.core:core-ktx:1.3.2'
     implementation 'androidx.appcompat:appcompat:1.2.0'
-    // implementation "androidx.fragment:fragment-ktx:1.3.0-beta02"
+    implementation "androidx.fragment:fragment-ktx:1.3.0-beta02"
     implementation 'com.google.android.material:material:1.2.1'
     implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
     implementation 'androidx.preference:preference-ktx:1.1.1'
@@ -68,4 +68,8 @@ dependencies {
     implementation 'androidx.startup:startup-runtime:1.0.0'
 
     implementation 'com.youth.banner:banner:2.1.0'
+
+    def refresh_version = '2.0.1'
+    implementation "com.scwang.smart:refresh-layout-kernel:$refresh_version"
+    implementation  "com.scwang.smart:refresh-header-classics:$refresh_version"
 }

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

@@ -15,7 +15,6 @@ import kotlinx.coroutines.launch
 import retrofit2.Call
 import retrofit2.Callback
 import retrofit2.Response
-import retrofit2.Retrofit
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
 
@@ -57,7 +56,7 @@ object FuelInfoApi {
             }
             if (isLocalAdd) setLocalBarcodeMap(localBarcodeMap)
 
-            nozzleInfoList.sortBy { it.siteId }
+            nozzleInfoList.sortBy { it.physicalId }
             it.resume(DFSResult.success(nozzleInfoList))
         }
     }

+ 1 - 1
app/src/main/java/com/doverfuelingsolutions/issp/api/entity/NozzleInfo.kt

@@ -2,7 +2,7 @@ package com.doverfuelingsolutions.issp.api.entity
 
 data class NozzleInfo(
     val pumpId: Int,
-    val siteId: Int,
+    val physicalId: Int,
     val barcodeId: Int,
     val barcodeName: String,
 )

+ 7 - 2
app/src/main/java/com/doverfuelingsolutions/issp/fusion/FusionManager.kt

@@ -30,7 +30,7 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
     var stateFusion: FdcClient.FdcClientState = FdcClient.FdcClientState.Stopped
     private const val timeoutMax = 4000
     private var isLogin = false
-    private val nozzleList = arrayListOf<NozzleInfo>()
+    val nozzleList = arrayListOf<NozzleInfo>()
 
     private val coroutineIO = CoroutineScope(Dispatchers.IO)
 
@@ -103,6 +103,9 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
         }
     }
 
+    /**
+     * 初始化流程中的登录+获取油品信息
+     */
     fun loginFetchInfo() {
         coroutineIO.launch {
             if (!isLogin) {
@@ -121,7 +124,7 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
                 }
             }
 
-            val resultNozzles = FuelInfoApi.getNozzleInfo()
+            val resultNozzles = requestNozzleInfo()
             if (resultNozzles.success && resultNozzles.data != null) {
                 if (nozzleList.isNotEmpty()) nozzleList.clear()
                 nozzleList.addAll(resultNozzles.data)
@@ -132,6 +135,8 @@ object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
         }
     }
 
+    suspend fun requestNozzleInfo() = FuelInfoApi.getNozzleInfo()
+
     private suspend fun login() = suspendCoroutine<Boolean> {
         val port = SPUtil.getString(SPKeys.MIDDLE_PORT).toInt()
         FdcClient.getDefault().sendLogonRequestAsync(port, port, "00.07", { _, response ->

+ 70 - 29
app/src/main/java/com/doverfuelingsolutions/issp/view/MainActivity.kt

@@ -9,6 +9,8 @@ import androidx.activity.viewModels
 import androidx.appcompat.app.AppCompatActivity
 import androidx.databinding.DataBindingUtil
 import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentResultListener
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.lifecycleScope
@@ -20,8 +22,8 @@ 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.utils.log.DFSLog
 import com.doverfuelingsolutions.issp.view.fragment.FragmentHolder
+import com.doverfuelingsolutions.issp.view.fragment.FragmentNozzle
 import com.doverfuelingsolutions.issp.view.fragment.FragmentSelect
 import com.wayne.www.waynelib.fdc.FdcClient
 import com.youth.banner.adapter.BannerImageAdapter
@@ -29,12 +31,18 @@ import com.youth.banner.holder.BannerImageHolder
 import kotlinx.coroutines.launch
 import java.io.File
 
-class MainActivity : AppCompatActivity(), OnFusionEvent, View.OnLongClickListener {
+class MainActivity : AppCompatActivity(),
+    OnFusionEvent,
+    View.OnLongClickListener,
+    FragmentResultListener {
 
     companion object {
         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) }
@@ -42,6 +50,8 @@ class MainActivity : AppCompatActivity(), OnFusionEvent, View.OnLongClickListene
 
     private val fragmentHolder = FragmentHolder()
     private val fragmentSelect = FragmentSelect()
+    private val fragmentNozzle = FragmentNozzle()
+    private var isFragmentHoldBack = true
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -65,26 +75,50 @@ class MainActivity : AppCompatActivity(), OnFusionEvent, View.OnLongClickListene
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         super.onActivityResult(requestCode, resultCode, data)
 
-        when (requestCode) {
-            PreferenceActivity.codeRequestResult -> {
-                data?.let {
-                    val isMiddleModified = it.getBooleanExtra(PreferenceActivity.isMiddleModified, false)
-                    val isFuelModified = it.getBooleanExtra(PreferenceActivity.isFuelModified, false)
-                    if (!isFuelModified && !isMiddleModified) return@let
-
-                    setFragment(fragmentHolder)
-                    if (isMiddleModified || FusionManager.stateFusion != FdcClient.FdcClientState.Connected) {
-                        fragmentHolder.loading(StringUtil.get(R.string.in_reconnect_fusion))
-                        FusionManager.restart()
-                    } else if (isFuelModified && FusionManager.stateFusion == FdcClient.FdcClientState.Connected) {
-                        fragmentHolder.loading(StringUtil.get(R.string.in_get_fuel))
-                        FusionManager.loginFetchInfo()
+        lifecycleScope.launch {
+            when (requestCode) {
+                PreferenceActivity.codeRequestResult -> {
+                    data?.let {
+                        val isMiddleModified = it.getBooleanExtra(PreferenceActivity.isMiddleModified, false)
+                        val isFuelModified = it.getBooleanExtra(PreferenceActivity.isFuelModified, false)
+                        if (!isFuelModified && !isMiddleModified) return@let
+
+                        setFragment(fragmentHolder)
+                        if (isMiddleModified || FusionManager.stateFusion != FdcClient.FdcClientState.Connected) {
+                            fragmentHolder.loading(StringUtil.get(R.string.in_reconnect_fusion))
+                            FusionManager.restart()
+                        } else if (isFuelModified && FusionManager.stateFusion == FdcClient.FdcClientState.Connected) {
+                            fragmentHolder.loading(StringUtil.get(R.string.in_get_fuel))
+                            FusionManager.loginFetchInfo()
+                        }
                     }
                 }
             }
         }
     }
 
+    override fun onBackPressed() {
+        if (isFragmentHoldBack) super.onBackPressed()
+    }
+
+    override fun onFragmentResult(requestKey: String, result: Bundle) {
+        when (requestKey) {
+            keyOnBack -> {
+                supportFragmentManager.popBackStack()
+            }
+            keyOnHome -> {
+                if (supportFragmentManager.backStackEntryCount > 0) {
+                    supportFragmentManager.popBackStack(supportFragmentManager.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+                }
+            }
+            FragmentSelect.keyOnSelect -> {
+                val isNozzle = result.getBoolean(FragmentSelect.keyIsNozzle)
+                // TODO 油品和枪号选择界面
+                setFragment(if (isNozzle) fragmentNozzle else fragmentHolder, true)
+            }
+        }
+    }
+
     override fun onLongClick(v: View?): Boolean {
         when (v) {
             binding.clock -> {
@@ -146,31 +180,38 @@ class MainActivity : AppCompatActivity(), OnFusionEvent, View.OnLongClickListene
                 }
         }
         binding.clock.setOnLongClickListener(this)
-    }
 
-    private fun initFusion() {
+        supportFragmentManager.setFragmentResultListener(FragmentSelect.keyOnSelect, this, this)
+        supportFragmentManager.setFragmentResultListener(keyOnBack, this, this)
+        supportFragmentManager.setFragmentResultListener(keyOnHome, this, this)
         setFragment(fragmentHolder)
+    }
 
+    private fun initFusion() {
         FusionManager.onFusionEvent = this
         lifecycle.addObserver(FusionManager)
     }
 
     private fun setFragment(fragment: Fragment, back: Boolean = false) {
         val contain = supportFragmentManager.fragments.contains(fragment)
-        val show = fragment.isHidden
+        val show = !fragment.isHidden
 
-        if (contain && !show) return
+        if (contain && show) return
 
-        val transaction = supportFragmentManager.beginTransaction()
-        supportFragmentManager.fragments.forEach {
-            if (fragment != it && !it.isHidden) transaction.hide(it)
-        }
-        if (!contain) {
-            transaction.add(R.id.fragmentBox, fragment)
-        } else if (fragment.isHidden) {
-            transaction.show(fragment)
+        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)
+            }
+            if (back) {
+                addToBackStack(fragment.javaClass.simpleName)
+            }
+            commit()
         }
-        transaction.commit()
     }
 
     class MainViewModel : ViewModel() {

+ 27 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/adapter/NozzleChooseAdapter.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_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)
+}

+ 75 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentNozzle.kt

@@ -0,0 +1,75 @@
+package com.doverfuelingsolutions.issp.view.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+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.scwang.smart.refresh.header.ClassicsHeader
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+
+class FragmentNozzle : Fragment() {
+
+    private lateinit var binding: FragmentNozzleBinding
+    private val nozzleAdapter: NozzleChooseAdapter by lazy {
+        NozzleChooseAdapter(FusionManager.nozzleList.map { it.physicalId }, this::select)
+    }
+
+    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.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())
+    }
+
+    private fun handleNozzleRefresh() {
+        GlobalScope.launch(Dispatchers.Main) {
+            val resultNozzles = FusionManager.requestNozzleInfo()
+            if (resultNozzles.success && resultNozzles.data != null) {
+                if (FusionManager.nozzleList.isNotEmpty()) FusionManager.nozzleList.clear()
+                FusionManager.nozzleList.addAll(resultNozzles.data)
+                binding.nozzleList.adapter?.notifyDataSetChanged()
+                binding.smartRefreshLayout.finishRefresh(200, true, false)
+            } else {
+                binding.smartRefreshLayout.finishRefresh(200, false, true)
+            }
+        }
+    }
+
+    private fun select(nozzle: Int) {
+        DFSLog.i("nozzle: $nozzle")
+    }
+
+    class FragmentNozzleViewModel : ViewModel() {}
+}

+ 13 - 6
app/src/main/java/com/doverfuelingsolutions/issp/view/fragment/FragmentSelect.kt

@@ -4,9 +4,10 @@ import android.os.Bundle
 import android.view.LayoutInflater
 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.activityViewModels
+import androidx.fragment.app.setFragmentResult
 import androidx.fragment.app.viewModels
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
@@ -14,10 +15,16 @@ import com.doverfuelingsolutions.issp.DFSApplication
 import com.doverfuelingsolutions.issp.R
 import com.doverfuelingsolutions.issp.data.GlobalData
 import com.doverfuelingsolutions.issp.databinding.FragmentSearchTypeBinding
-import com.doverfuelingsolutions.issp.utils.log.DFSLog
-import com.doverfuelingsolutions.issp.view.MainActivity
 
-class FragmentSelect : Fragment(), View.OnClickListener, View.OnLongClickListener {
+class FragmentSelect : Fragment(),
+    View.OnClickListener,
+    View.OnLongClickListener {
+
+    companion object {
+        const val keyOnSelect = "FragmentSelect"
+        const val keyIsNozzle = "isNozzle"
+        const val keyIsOil = "isOil"
+    }
 
     private val viewModel: SearchTypeViewModel by viewModels()
 
@@ -36,8 +43,8 @@ class FragmentSelect : Fragment(), View.OnClickListener, View.OnLongClickListene
 
     override fun onClick(v: View?) {
         when (v) {
-            binding.chooseNozzle -> DFSLog.i("chooseNozzle")
-            binding.chooseOil -> DFSLog.i("chooseOil")
+            binding.chooseNozzle -> setFragmentResult(keyOnSelect, bundleOf(Pair(keyIsNozzle, true)))
+            binding.chooseOil -> setFragmentResult(keyOnSelect, bundleOf(Pair(keyIsOil, true)))
         }
     }
 

+ 98 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/widget/FragmentToolbar.kt

@@ -0,0 +1,98 @@
+package com.doverfuelingsolutions.issp.view.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.widget.FrameLayout
+import com.doverfuelingsolutions.issp.R
+import kotlinx.android.synthetic.main.widget_fragment_toolbar.view.*
+import kotlinx.coroutines.*
+
+class FragmentToolbar(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
+
+    val view: View = View.inflate(context, R.layout.widget_fragment_toolbar, this)
+
+    private var isBackVisible = true
+        set(value) {
+            field = value
+            buttonBack.visibility = if (value) VISIBLE else GONE
+        }
+    private var isHomeVisible = true
+        set(value) {
+            field = value
+            buttonHome.visibility = if (value) VISIBLE else GONE
+        }
+    private var title = ""
+        set(value) {
+            field = value
+            materialToolbar.title = value
+        }
+
+    private var countdownTime: Int = 60
+    private var countdownAutoStart = true
+    private var mOnCountdownFinish: (() -> Unit)? = null
+
+    private var coroutineMain = CoroutineScope(Dispatchers.Main)
+    private var coroutineJob: Job? = null
+
+    private var backHandler: (() -> Unit)? = null
+    private var homeHandler: (() -> Unit)? = null
+
+    init {
+        context.theme.obtainStyledAttributes(attrs, R.styleable.FragmentToolbar, 0, 0).apply {
+            try {
+                isBackVisible = getBoolean(R.styleable.FragmentToolbar_backVisible, true)
+                isHomeVisible = getBoolean(R.styleable.FragmentToolbar_homeVisible, true)
+                title = getString(R.styleable.FragmentToolbar_title) ?: ""
+                countdownTime = getInteger(R.styleable.FragmentToolbar_countdownTime, 60)
+                countdownAutoStart = getBoolean(R.styleable.FragmentToolbar_countdownAutoStart, true)
+            } finally {
+                recycle()
+            }
+        }
+
+        buttonBack.setOnClickListener { backHandler?.invoke() }
+        buttonHome.setOnClickListener { homeHandler?.invoke() }
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        if (countdownAutoStart) countdown(countdownTime)
+    }
+
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+
+        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()
+            }
+        }
+    }
+
+    fun setOnCountdownFinish(listener: () -> Unit) {
+        mOnCountdownFinish = listener
+    }
+
+    fun setBackHandler(handler: () -> Unit) {
+        backHandler = handler
+    }
+
+    fun setHomeHandler(handler: () -> Unit) {
+        homeHandler = handler
+    }
+}

+ 5 - 0
app/src/main/res/color/primary_variant.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/colorPrimary" android:state_focused="false" />
+    <item android:color="@color/colorPrimaryVariant" android:state_focused="true" />
+</selector>

+ 9 - 0
app/src/main/res/drawable/ic_back.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="200dp"
+    android:height="200dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024">
+  <path
+      android:pathData="M832,522.67H255.21l247.65,-252.79a32.02,32.02 0,0 0,-45.72 -44.8L135.68,553.23l322.35,304.68c6.19,5.86 14.08,8.75 21.97,8.75a31.99,31.99 0,0 0,21.98 -55.24L264.21,586.67H832a32,32 0,1 0,0 -64z"
+      android:fillColor="#ffffff"/>
+</vector>

+ 9 - 0
app/src/main/res/drawable/ic_home.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="200dp"
+    android:height="200dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024">
+  <path
+      android:pathData="M508.11,0c-279.13,0 -505.48,226.35 -505.48,505.48s226.35,505.48 505.48,505.48 505.48,-226.35 505.48,-505.48 -226.3,-505.48 -505.48,-505.48zM743,791.08h-149.98v-158.97L435.17,632.1v156.75L287.52,788.85s-2.22,-234.49 -3.39,-234.49l231.16,-173.63 227.72,173.63v236.72zM818.57,563.36l-303.34,-240.15 -307.79,237.93L207.45,464.18l306.67,-239.04 304.45,239.04v99.18z"
+      android:fillColor="#ffffff"/>
+</vector>

+ 9 - 0
app/src/main/res/drawable/ic_homepage.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="200dp"
+    android:height="200dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024">
+  <path
+      android:pathData="M569.8,21.27a82.07,82.07 0,0 0,-110.28 0L13.57,444.72a41.14,41.14 0,0 0,55.09 61.11l7.09,-6.73V941.06a82.28,82.28 0,0 0,82.28 82.38h239.44v-247.71a57.93,57.93 0,0 1,57.96 -58.01h112.67a57.93,57.93 0,0 1,57.96 58.01v247.71h245.12a82.28,82.28 0,0 0,82.3 -82.38V504.5c18.28,16.54 44.21,15.28 59.42,-1.66a41.14,41.14 0,0 0,-2.97 -58.11L569.8,21.3z"
+      android:fillColor="#ffffff"/>
+</vector>

+ 17 - 0
app/src/main/res/layout/adapter_choose_nozzle.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat style="@style/fullWidth"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingTop="30dp"
+    android:paddingHorizontal="4dp">
+
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/chooseNozzleNum"
+        android:text="@string/example_num"
+        android:gravity="center"
+        android:paddingTop="20dp"
+        android:paddingBottom="20dp"
+        android:textColor="@color/white"
+        android:textSize="44sp"
+        android:background="@color/colorPrimary"
+        style="@style/fullWidth" />
+</androidx.appcompat.widget.LinearLayoutCompat>

+ 41 - 0
app/src/main/res/layout/fragment_nozzle.xml

@@ -0,0 +1,41 @@
+<?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>
+
+    </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/query_by_nozzle" />
+
+        <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:paddingHorizontal="10dp"
+                android:id="@+id/nozzleList"
+                style="@style/match" />
+        </com.scwang.smart.refresh.layout.SmartRefreshLayout>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 52 - 0
app/src/main/res/layout/widget_fragment_toolbar.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="@style/fullWidth"
+    android:elevation="10dp"
+    app:elevation="10dp">
+
+    <androidx.appcompat.widget.LinearLayoutCompat
+        style="@style/fullWidth"
+        android:layout_marginStart="20dp"
+        android:layout_marginEnd="20dp"
+        android:gravity="center_vertical"
+        android:weightSum="1">
+
+        <ImageView
+            android:id="@+id/buttonBack"
+            android:layout_width="30dp"
+            android:layout_height="30dp"
+            android:layout_centerVertical="true"
+            android:contentDescription="@string/back"
+            android:src="@drawable/ic_back"
+            app:tint="@color/white" />
+
+        <com.google.android.material.appbar.MaterialToolbar
+            android:id="@+id/materialToolbar"
+            style="@style/Widget.MaterialComponents.Toolbar.Primary"
+            android:layout_width="0dp"
+            android:layout_height="?attr/actionBarSize"
+            android:layout_weight="1"
+            android:elevation="0dp"
+            app:title="@string/app_name"
+            app:titleTextColor="@color/white" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/countdownNum"
+            style="@style/wrap"
+            android:text="@string/example_num"
+            android:textSize="18sp"
+            android:textColor="@color/white"/>
+
+        <ImageView
+            android:id="@+id/buttonHome"
+            android:layout_width="22dp"
+            android:layout_height="22dp"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="30dp"
+            android:contentDescription="@string/homepage"
+            android:src="@drawable/ic_homepage"
+            app:tint="@color/white" />
+
+    </androidx.appcompat.widget.LinearLayoutCompat>
+</com.google.android.material.appbar.AppBarLayout>

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

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <declare-styleable name="FragmentToolbar">
+        <attr name="backVisible" format="boolean"/>
+        <attr name="homeVisible" format="boolean"/>
+        <attr name="title" format="string"/>
+        <attr name="countdownTime" format="integer"/>
+        <attr name="countdownAutoStart" format="boolean"/>
+    </declare-styleable>
+</resources>

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

@@ -11,6 +11,9 @@
     <color name="colorPrimaryNight">#FFBB86FC</color>
     <color name="colorSecondaryNight">#FF03DAC5</color>
 
+    <color name="colorLight">#E7F8FC</color>
+    <color name="colorTransparent">#00000000</color>
+
     <color name="gray">#FF888888</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>

+ 5 - 0
app/src/main/res/values/strings.xml

@@ -12,6 +12,8 @@
     <string name="cancel">取消</string>
     <string name="go">前往</string>
     <string name="exit">退出</string>
+    <string name="back">后退</string>
+    <string name="homepage">首页</string>
 
     <string name="in_login">登录中&#8230;</string>
     <string name="in_loading">加载中&#8230;</string>
@@ -69,8 +71,11 @@
     <string name="go_check_fuel">前往检查加油信息</string>
     <string name="retry_login">重试登录</string>
 
+    <string name="query_by_nozzle">根据枪号查询</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_company_name">托肯恒山科技有限公司</string>
+    <string name="example_num">55</string>
 </resources>

+ 4 - 0
app/src/main/res/values/styles-md-fix.xml

@@ -4,4 +4,8 @@
     <style name="TextInputLabel" parent="@style/TextAppearance.MaterialComponents.Caption">
         <item name="android:textSize">16sp</item>
     </style>
+
+    <style name="FragmentTitle" parent="@style/TextAppearance.MaterialComponents.Headline6">
+        <item name="android:textAlignment">center</item>
+    </style>
 </resources>