2 Commity 9cef8db292 ... 9084d8e66a

Autor SHA1 Wiadomość Data
  zhaoyadi 9084d8e66a 修改介绍页 7 miesięcy temu
  zhaoyadi a69a6c09e2 权限申请前移 7 miesięcy temu
44 zmienionych plików z 537 dodań i 196 usunięć
  1. 0 4
      xiaodou/build.gradle
  2. 2 2
      xiaodou/src/main/AndroidManifest.xml
  3. 6 9
      xiaodou/src/main/java/com/luojigou/product/ProductSelectActivity.kt
  4. 0 27
      xiaodou/src/main/java/com/luojigou/product/base/AbsIntroduceActivity.kt
  5. 13 0
      xiaodou/src/main/java/com/luojigou/product/base/BaseActivity.kt
  6. 6 0
      xiaodou/src/main/java/com/luojigou/product/base/BaseFragment.kt
  7. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDBLEConnectStatus.kt
  8. 16 0
      xiaodou/src/main/java/com/luojigou/product/ble/XDBLEController.kt
  9. 29 0
      xiaodou/src/main/java/com/luojigou/product/ble/XDBLEUtils.kt
  10. 1 17
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEActivity.kt
  11. 5 5
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEAdapter.kt
  12. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEDevice.kt
  13. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEDisableFragment.kt
  14. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEGattCallback.kt
  15. 17 9
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLENormalFragment.kt
  16. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLENotSupportFragment.kt
  17. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEPermissionFragment.kt
  18. 1 1
      xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEStatus.kt
  19. 83 0
      xiaodou/src/main/java/com/luojigou/product/intro/AbsIntroduceActivity.kt
  20. 39 0
      xiaodou/src/main/java/com/luojigou/product/intro/IntroduceNormalFragment.kt
  21. 30 0
      xiaodou/src/main/java/com/luojigou/product/intro/IntroducePermissionFragment.kt
  22. 30 0
      xiaodou/src/main/java/com/luojigou/product/intro/IntroducePermissionManualFragment.kt
  23. 68 0
      xiaodou/src/main/java/com/luojigou/product/utils/PermissionUtils.kt
  24. 1 16
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiActivity.kt
  25. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiAdapter.kt
  26. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiDisableFragment.kt
  27. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiInfo.kt
  28. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiNormalFragment.kt
  29. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiPasswordFragment.kt
  30. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiPasswordWithSsidFragment.kt
  31. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiPermissionFragment.kt
  32. 1 1
      xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiStatus.kt
  33. 1 12
      xiaodou/src/main/java/com/luojigou/product/xiaodou/XDConnectActivity.kt
  34. 3 3
      xiaodou/src/main/java/com/luojigou/product/xiaodou/XDConnectService.kt
  35. 2 1
      xiaodou/src/main/java/com/luojigou/product/xiaodou/XDIntroduceActivity.kt
  36. 0 7
      xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDBLEController.kt
  37. 0 55
      xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDBLEUtils.kt
  38. 1 1
      xiaodou/src/main/java/com/luojigou/product/xiaodou/connect/XDConnectPasswordFragment.kt
  39. 1 1
      xiaodou/src/main/java/com/luojigou/product/xiaodou/connect/XDConnectPasswordSsidFragment.kt
  40. 10 0
      xiaodou/src/main/res/layout/activity_introduce.xml
  41. 0 0
      xiaodou/src/main/res/layout/fragment_introduce_normal.xml
  42. 67 0
      xiaodou/src/main/res/layout/fragment_introduce_permission.xml
  43. 74 0
      xiaodou/src/main/res/layout/fragment_introduce_permission_manual.xml
  44. 17 12
      xiaodou/src/main/res/layout/item_product.xml

+ 0 - 4
xiaodou/build.gradle

@@ -33,10 +33,6 @@ android {
                     'libs',
                     'src/main/cpp/libs'
             ]
-
-            res{
-                srcDir("src/main/res-xiaodou")
-            }
         }
     }
     buildFeatures {

+ 2 - 2
xiaodou/src/main/AndroidManifest.xml

@@ -43,11 +43,11 @@
             android:theme="@style/XDTheme" />
 
         <activity
-            android:name=".xiaodou.XDScanBLEActivity"
+            android:name=".ble.XDScanBLEActivity"
             android:theme="@style/XDTheme" />
 
         <activity
-            android:name=".xiaodou.XDScanWifiActivity"
+            android:name=".wifi.XDScanWifiActivity"
             android:theme="@style/XDTheme" />
 
         <activity

+ 6 - 9
xiaodou/src/main/java/com/luojigou/product/ProductSelectActivity.kt

@@ -4,21 +4,21 @@ import android.content.Intent
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.LayoutManager
 import com.luojigou.product.base.BaseActivity
 import com.luojigou.product.xiaodou.XDIntroduceActivity
 import com.google.android.material.R.drawable as MaterialDrawable
 
 class ProductSelectActivity : BaseActivity() {
     private lateinit var productAdapter: ProductAdapter
-    private lateinit var productLayoutManager: GridLayoutManager
+    private lateinit var productLayoutManager: LayoutManager
     private lateinit var productRecyclerView: RecyclerView
 
     private val productList = listOf(
-        ProductModel(0, "小逗AI学习机", MaterialDrawable.ic_clock_black_24dp),
-        ProductModel(1, "中逗AI学习机", MaterialDrawable.ic_keyboard_black_24dp),
-        ProductModel(2, "大逗AI学习机", MaterialDrawable.material_ic_calendar_black_24dp),
+        ProductModel(0, "小逗AI思维机", MaterialDrawable.ic_clock_black_24dp),
+        ProductModel(1, "智能指读学习机", MaterialDrawable.ic_keyboard_black_24dp),
     )
 
     override fun onCreate(savedInstanceState: android.os.Bundle?) {
@@ -33,12 +33,9 @@ class ProductSelectActivity : BaseActivity() {
                 0 -> startActivity(Intent(this, XDIntroduceActivity::class.java))
                 1 -> { // TODO: 跳转到中逗AI学习机
                 }
-
-                2 -> { // TODO: 跳转到大逗AI学习机
-                }
             }
         }
-        productLayoutManager = GridLayoutManager(this, 2)
+        productLayoutManager = LinearLayoutManager(this)
         productRecyclerView.layoutManager = productLayoutManager
         productRecyclerView.adapter = productAdapter
     }

+ 0 - 27
xiaodou/src/main/java/com/luojigou/product/base/AbsIntroduceActivity.kt

@@ -1,27 +0,0 @@
-package com.luojigou.product.base
-
-import android.view.View
-import android.widget.Button
-import com.luojigou.product.R
-
-abstract class AbsIntroduceActivity : BaseActivity(), View.OnClickListener {
-
-    private lateinit var buttonStart: Button
-
-    override fun onCreate(savedInstanceState: android.os.Bundle?) {
-        super.onCreate(savedInstanceState)
-        setTitle("连接引导")
-        buttonStart = findViewById(R.id.btn_intro_start)
-        buttonStart.setOnClickListener(this)
-    }
-
-    override fun getLayoutId(): Int = R.layout.activity_xd_introduce
-
-    override fun onClick(v: View?) {
-        when (v?.id) {
-            R.id.btn_intro_start -> startActivity()
-        }
-    }
-
-    abstract fun startActivity()
-}

+ 13 - 0
xiaodou/src/main/java/com/luojigou/product/base/BaseActivity.kt

@@ -9,6 +9,8 @@ import android.os.Bundle
 import androidx.annotation.LayoutRes
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import com.luojigou.product.R
 import com.luojigou.product.xiaodou.XDConnectActivity
 import com.luojigou.product.xiaodou.XDConnectService
 
@@ -37,6 +39,17 @@ abstract class BaseActivity : AppCompatActivity() {
         }
     }
 
+    protected fun setFragment(fragment: Fragment) {
+        val fragmentManager = supportFragmentManager;
+        val beginTransaction = fragmentManager.beginTransaction()
+        if (fragmentManager.findFragmentById(R.id.fragment_container) == null) {
+            beginTransaction.add(R.id.fragment_container, fragment)
+        } else {
+            beginTransaction.replace(R.id.fragment_container, fragment)
+        }
+        beginTransaction.commit()
+    }
+
     private fun setupFinishReceiver() {
         if (!_isFinishing && this !is XDConnectActivity) {
             val intentFilter = IntentFilter()

+ 6 - 0
xiaodou/src/main/java/com/luojigou/product/base/BaseFragment.kt

@@ -0,0 +1,6 @@
+package com.luojigou.product.base
+
+import androidx.fragment.app.Fragment
+
+open class BaseFragment : Fragment() {
+}

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDBLEConnectStatus.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDBLEConnectStatus.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 sealed class XDBLEConnectStatus() {
     object Waiting : XDBLEConnectStatus()

+ 16 - 0
xiaodou/src/main/java/com/luojigou/product/ble/XDBLEController.kt

@@ -0,0 +1,16 @@
+package com.luojigou.product.ble
+
+import android.bluetooth.le.ScanFilter
+import android.bluetooth.le.ScanSettings
+import android.content.Context
+
+class XDBLEController(private val context: Context) {
+    @Volatile
+    private var bleScanning = false
+
+    private val bleScanSettings = ScanSettings.Builder()
+        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+        .build()
+
+    private val bleScanFilters = mutableListOf<ScanFilter>()
+}

+ 29 - 0
xiaodou/src/main/java/com/luojigou/product/ble/XDBLEUtils.kt

@@ -0,0 +1,29 @@
+package com.luojigou.product.ble
+
+import android.util.Log
+
+object XDBLEUtils {
+    const val START_FIRST: Byte = 0x7E.toByte()
+    const val START_SECOND: Byte = 0x01.toByte()
+    const val END_BYTE: Byte = 255.toByte()
+    fun parseConnectResponse(data: ByteArray): Int {
+        if (data.size < 17) return -99
+        Log.d("XDBLEUtils", "parseConnectResponse: ${data.decodeToString()}")
+        return data[16] - '0'.code
+    }
+
+    fun convertToConnectRequest(ssid: String, password: String): ByteArray {
+        val ssidArray = ssid.toByteArray(Charsets.UTF_8)
+        val passwordArray = password.toByteArray(Charsets.UTF_8)
+        val byteArray =  byteArrayOf(
+            START_FIRST,
+            START_SECOND,
+            ssidArray.size.toByte(),
+            *ssidArray,
+            passwordArray.size.toByte(),
+            *passwordArray,
+            END_BYTE
+        )
+        return byteArray
+    }
+}

+ 1 - 17
xiaodou/src/main/java/com/luojigou/product/xiaodou/XDScanBLEActivity.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEActivity.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou
+package com.luojigou.product.ble
 
 import android.Manifest
 import android.bluetooth.BluetoothAdapter
@@ -14,11 +14,6 @@ import androidx.core.app.ActivityCompat
 import androidx.fragment.app.Fragment
 import com.luojigou.product.R
 import com.luojigou.product.base.BaseActivity
-import com.luojigou.product.xiaodou.ble.XDScanBLEDisableFragment
-import com.luojigou.product.xiaodou.ble.XDScanBLENormalFragment
-import com.luojigou.product.xiaodou.ble.XDScanBLENotSupportFragment
-import com.luojigou.product.xiaodou.ble.XDScanBLEPermissionFragment
-import com.luojigou.product.xiaodou.ble.XDScanBLEStatus
 
 
 class XDScanBLEActivity : BaseActivity(), XDScanBLEStatus.Host {
@@ -114,16 +109,5 @@ class XDScanBLEActivity : BaseActivity(), XDScanBLEStatus.Host {
         setFragment(normalFragment)
     }
 
-    private fun setFragment(fragment: Fragment) {
-        val fragmentManager = supportFragmentManager;
-        val beginTransaction = fragmentManager.beginTransaction()
-        if (fragmentManager.findFragmentById(R.id.fragment_container) == null) {
-            beginTransaction.add(R.id.fragment_container, fragment)
-        } else {
-            beginTransaction.replace(R.id.fragment_container, fragment)
-        }
-        beginTransaction.commit()
-    }
-
     override fun onStatusChanged(status: XDScanBLEStatus) = checkBluetoothPermissionAndEnable()
 }

+ 5 - 5
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLEAdapter.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEAdapter.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.annotation.SuppressLint
 import android.view.LayoutInflater
@@ -13,9 +13,9 @@ import com.luojigou.product.R
 
 @SuppressLint("MissingPermission")
 class XDScanBLEAdapter(
-    private val itemClickListener: (com.luojigou.product.xiaodou.ble.XDScanBLEDevice) -> Unit,
+    private val itemClickListener: (XDScanBLEDevice) -> Unit,
 ) : RecyclerView.Adapter<XDScanBLEAdapter.ViewHolder>() {
-    private val list: MutableSet<com.luojigou.product.xiaodou.ble.XDScanBLEDevice> = ArraySet()
+    private val list: MutableSet<XDScanBLEDevice> = ArraySet()
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
         val itemView: View =
@@ -39,13 +39,13 @@ class XDScanBLEAdapter(
     }
 
     @SuppressLint("NotifyDataSetChanged")
-    fun setValues(values: List<com.luojigou.product.xiaodou.ble.XDScanBLEDevice>) {
+    fun setValues(values: List<XDScanBLEDevice>) {
         list.clear()
         list.addAll(values)
         notifyDataSetChanged()
     }
 
-    fun addValue(value: com.luojigou.product.xiaodou.ble.XDScanBLEDevice) {
+    fun addValue(value: XDScanBLEDevice) {
         list.add(value)
         notifyItemChanged(list.size - 1)
     }

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLEDevice.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEDevice.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.bluetooth.BluetoothDevice
 import android.os.Parcel

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLEDisableFragment.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEDisableFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.annotation.SuppressLint
 import android.bluetooth.BluetoothAdapter

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLEGattCallback.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEGattCallback.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.annotation.SuppressLint
 import android.bluetooth.BluetoothGattCallback

+ 17 - 9
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLENormalFragment.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLENormalFragment.kt

@@ -1,10 +1,11 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.annotation.SuppressLint
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.os.Build
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
@@ -18,12 +19,12 @@ import androidx.fragment.app.Fragment
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.luojigou.product.R
+import com.luojigou.product.wifi.XDScanWifiActivity
 import com.luojigou.product.xiaodou.XDConnectService
 import com.luojigou.product.xiaodou.XDConnectService.Companion.ACTION_SCAN_RESULT
 import com.luojigou.product.xiaodou.XDConnectService.Companion.ACTION_SCAN_RESULT_DATA
 import com.luojigou.product.xiaodou.XDConnectService.Companion.ACTION_START_SCAN
 import com.luojigou.product.xiaodou.XDConnectService.Companion.ACTION_STOP_SCAN
-import com.luojigou.product.xiaodou.XDScanWifiActivity
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.MainScope
 
@@ -89,21 +90,28 @@ class XDScanBLENormalFragment(private val host: XDScanBLEStatus.Host) : Fragment
                     }
 
                     ACTION_SCAN_RESULT -> {
-                        val list = intent.getParcelableArrayListExtra(
-                            ACTION_SCAN_RESULT_DATA, XDScanBLEDevice::class.java
-                        )
+                        val list = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                            intent.getParcelableArrayListExtra(
+                                ACTION_SCAN_RESULT_DATA, XDScanBLEDevice::class.java
+                            )
+                        } else {
+                            intent.getParcelableArrayListExtra(ACTION_SCAN_RESULT_DATA)
+                        }
 
                         if (list != null) {
                             setAdapterValue(list)
 
                             if (list.size == 1) {
-                                val intent = Intent(XDConnectService.ACTION_SELECT_BLE_DEVICE)
-                                intent.putExtra(
+                                val selectIntent = Intent(XDConnectService.ACTION_SELECT_BLE_DEVICE)
+                                selectIntent.putExtra(
                                     XDConnectService.ACTION_SELECT_BLE_DEVICE_DATA,
                                     list[0]
                                 )
-                                requireActivity().sendBroadcast(intent)
-                                XDScanWifiActivity.start(requireContext())
+
+                                activity?.run {
+                                    sendBroadcast(selectIntent)
+                                    XDScanWifiActivity.start(this)
+                                }
                             }
                         }
                     }

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLENotSupportFragment.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLENotSupportFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.os.Bundle
 import android.view.LayoutInflater

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLEPermissionFragment.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEPermissionFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 import android.Manifest
 import android.os.Build

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDScanBLEStatus.kt → xiaodou/src/main/java/com/luojigou/product/ble/XDScanBLEStatus.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.ble
+package com.luojigou.product.ble
 
 sealed class XDScanBLEStatus {
     object Disabled : XDScanBLEStatus()

+ 83 - 0
xiaodou/src/main/java/com/luojigou/product/intro/AbsIntroduceActivity.kt

@@ -0,0 +1,83 @@
+package com.luojigou.product.intro
+
+import android.content.Intent
+import android.net.Uri
+import android.provider.Settings
+import android.util.Log
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import com.luojigou.product.R
+import com.luojigou.product.base.BaseActivity
+import com.luojigou.product.utils.PermissionUtils
+
+
+abstract class AbsIntroduceActivity : BaseActivity(), IntroducePermissionFragment.Host,
+    IntroducePermissionManualFragment.Host {
+
+    private lateinit var permissionUtils: PermissionUtils
+
+    private lateinit var requestPermission: ActivityResultLauncher<Array<String>>
+
+
+    private var requesting = false
+    override fun onCreate(savedInstanceState: android.os.Bundle?) {
+        super.onCreate(savedInstanceState)
+        setTitle("连接引导")
+        requestPermission = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
+            if (it.all { permission -> permission.value }) {
+                setFragment(IntroduceNormalFragment())
+            } else {
+                setFragment(IntroducePermissionManualFragment(this))
+                Log.e("XDScanWifiActivity", "requestPermission: 权限被拒绝")
+            }
+
+            requesting = false
+        }
+        permissionUtils = PermissionUtils(this)
+    }
+
+    override fun onResume() {
+        super.onResume()
+        checkAndSetFragment()
+    }
+
+    private fun checkAndSetFragment() {
+        if (permissionUtils.checkAllPermissions()) {
+            setFragment(IntroduceNormalFragment())
+        } else {
+            if (permissionUtils.shouldShowRequestPermissionRationale()) {
+                setFragment(IntroducePermissionManualFragment(this))
+            } else {
+                setFragment(IntroducePermissionFragment(this))
+            }
+        }
+    }
+
+    override fun getLayoutId(): Int = R.layout.activity_introduce
+
+    override fun requestPermission() {
+        if (requesting) {
+            return;
+        }
+
+        requesting = true
+        requestPermission.launch(permissionUtils.requestAllPermissions())
+    }
+
+    override fun manualRequestPermission() {
+        try {
+            val settingsIntent = Intent()
+            settingsIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+            settingsIntent.addCategory(Intent.CATEGORY_DEFAULT)
+            settingsIntent.setData(Uri.parse("package:" + this.packageName))
+            settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
+            settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+            this.startActivity(settingsIntent)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    abstract fun startActivity()
+}

+ 39 - 0
xiaodou/src/main/java/com/luojigou/product/intro/IntroduceNormalFragment.kt

@@ -0,0 +1,39 @@
+package com.luojigou.product.intro
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import com.luojigou.product.R
+import com.luojigou.product.base.BaseFragment
+
+class IntroduceNormalFragment : BaseFragment(), View.OnClickListener {
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(
+            R.layout.fragment_introduce_normal,
+            container,
+            false,
+        )
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val buttonStart = view.findViewById<Button>(R.id.btn_intro_start)
+        buttonStart.setOnClickListener(this)
+    }
+
+    override fun onClick(v: View?) {
+        when (v?.id) {
+            R.id.btn_intro_start -> {
+                val activity = requireActivity() as AbsIntroduceActivity
+                activity.startActivity()
+            }
+        }
+    }
+}

+ 30 - 0
xiaodou/src/main/java/com/luojigou/product/intro/IntroducePermissionFragment.kt

@@ -0,0 +1,30 @@
+package com.luojigou.product.intro
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import com.luojigou.product.R
+import com.luojigou.product.base.BaseFragment
+
+class IntroducePermissionFragment(private val host: Host) : BaseFragment() {
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(R.layout.fragment_introduce_permission, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val button = view.findViewById<Button>(R.id.btn_intro_request)
+        button.setOnClickListener { host.requestPermission() }
+    }
+
+    interface Host {
+        fun requestPermission()
+    }
+}

+ 30 - 0
xiaodou/src/main/java/com/luojigou/product/intro/IntroducePermissionManualFragment.kt

@@ -0,0 +1,30 @@
+package com.luojigou.product.intro
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import com.luojigou.product.R
+import com.luojigou.product.base.BaseFragment
+
+class IntroducePermissionManualFragment (private val host: Host) : BaseFragment() {
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(R.layout.fragment_introduce_permission_manual, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val button = view.findViewById<Button>(R.id.btn_intro_request)
+        button.setOnClickListener { host.manualRequestPermission() }
+    }
+
+    interface Host {
+        fun manualRequestPermission()
+    }
+}

+ 68 - 0
xiaodou/src/main/java/com/luojigou/product/utils/PermissionUtils.kt

@@ -0,0 +1,68 @@
+package com.luojigou.product.utils
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.core.app.ActivityCompat
+
+internal class PermissionUtils(private val context: Context) {
+    companion object {
+        private val blePermissions: Array<String> by lazy {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                arrayOf(
+                    Manifest.permission.BLUETOOTH_SCAN,
+                    Manifest.permission.BLUETOOTH_ADVERTISE,
+                    Manifest.permission.BLUETOOTH_CONNECT
+                )
+            } else {
+                arrayOf(
+                    Manifest.permission.ACCESS_FINE_LOCATION
+                )
+            }
+        }
+
+        private val wifiPermissions: Array<String> by lazy {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                arrayOf(
+                    Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE
+                )
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                arrayOf(Manifest.permission.CHANGE_WIFI_STATE)
+            } else {
+                arrayOf()
+            }
+        }
+
+        private val notificationPermissions: Array<String> by lazy {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                arrayOf(Manifest.permission.POST_NOTIFICATIONS)
+            } else {
+                arrayOf()
+            }
+        }
+
+        private val permissions: Array<String> by lazy {
+            blePermissions + wifiPermissions + notificationPermissions
+        }
+    }
+
+    fun checkAllPermissions(): Boolean {
+        return permissions.find {
+            ActivityCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED
+        } == null
+    }
+
+    fun shouldShowRequestPermissionRationale(): Boolean {
+        return permissions.find {
+            !ActivityCompat.shouldShowRequestPermissionRationale(context as Activity, it)
+        } != null
+    }
+
+    fun requestAllPermissions(): Array<String> {
+        return permissions.filter {
+            ActivityCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED
+        }.toTypedArray()
+    }
+}

+ 1 - 16
xiaodou/src/main/java/com/luojigou/product/xiaodou/XDScanWifiActivity.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiActivity.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou
+package com.luojigou.product.wifi
 
 import android.content.BroadcastReceiver
 import android.content.Context
@@ -12,10 +12,6 @@ import android.os.Bundle
 import androidx.fragment.app.Fragment
 import com.luojigou.product.R
 import com.luojigou.product.base.BaseActivity
-import com.luojigou.product.xiaodou.wifi.XDScanWifiDisableFragment
-import com.luojigou.product.xiaodou.wifi.XDScanWifiNormalFragment
-import com.luojigou.product.xiaodou.wifi.XDScanWifiPermissionFragment
-import com.luojigou.product.xiaodou.wifi.XDScanWifiStatus
 
 class XDScanWifiActivity : BaseActivity(), XDScanWifiStatus.Host {
     companion object {
@@ -53,17 +49,6 @@ class XDScanWifiActivity : BaseActivity(), XDScanWifiStatus.Host {
 
     override fun getWifiManager(): WifiManager = wifiManager
 
-    private fun setFragment(fragment: Fragment) {
-        val fragmentManager = supportFragmentManager;
-        val beginTransaction = fragmentManager.beginTransaction()
-        if (fragmentManager.findFragmentById(R.id.fragment_container) == null) {
-            beginTransaction.add(R.id.fragment_container, fragment)
-        } else {
-            beginTransaction.replace(R.id.fragment_container, fragment)
-        }
-        beginTransaction.commit()
-    }
-
     override fun onStatusChanged(status: XDScanWifiStatus) =
         checkWifiPermissionAndEnable()
 

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiAdapter.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiAdapter.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.net.wifi.ScanResult
 import android.os.Build

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiDisableFragment.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiDisableFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.content.Intent
 import android.net.wifi.WifiManager

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiInfo.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiInfo.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.net.wifi.ScanResult
 import android.os.Build

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiNormalFragment.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiNormalFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.annotation.SuppressLint
 import android.content.BroadcastReceiver

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiPasswordFragment.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiPasswordFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.os.Bundle
 import android.view.LayoutInflater

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiPasswordWithSsidFragment.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiPasswordWithSsidFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.content.Context
 import android.os.Bundle

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiPermissionFragment.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiPermissionFragment.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.os.Build
 import android.os.Bundle

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/wifi/XDScanWifiStatus.kt → xiaodou/src/main/java/com/luojigou/product/wifi/XDScanWifiStatus.kt

@@ -1,4 +1,4 @@
-package com.luojigou.product.xiaodou.wifi
+package com.luojigou.product.wifi
 
 import android.net.wifi.WifiManager
 

+ 1 - 12
xiaodou/src/main/java/com/luojigou/product/xiaodou/XDConnectActivity.kt

@@ -19,7 +19,7 @@ import com.luojigou.product.xiaodou.connect.XDConnectPasswordFragment
 import com.luojigou.product.xiaodou.connect.XDConnectPasswordSsidFragment
 import com.luojigou.product.xiaodou.connect.XDConnectSuccessFragment
 import com.luojigou.product.xiaodou.connect.XDConnectTimeoutFragment
-import com.luojigou.product.xiaodou.wifi.ssid
+import com.luojigou.product.wifi.ssid
 
 class XDConnectActivity : BaseActivity() {
     companion object {
@@ -94,17 +94,6 @@ class XDConnectActivity : BaseActivity() {
 
     override fun getLayoutId(): Int = R.layout.activity_connect
 
-    private fun setFragment(fragment: Fragment) {
-        val fragmentManager = supportFragmentManager;
-        val beginTransaction = fragmentManager.beginTransaction()
-        if (fragmentManager.findFragmentById(R.id.fragment_container) == null) {
-            beginTransaction.add(R.id.fragment_container, fragment)
-        } else {
-            beginTransaction.replace(R.id.fragment_container, fragment)
-        }
-        beginTransaction.commit()
-    }
-
     override fun onDestroy() {
         unregisterReceiver(connectReceiver)
         super.onDestroy()

+ 3 - 3
xiaodou/src/main/java/com/luojigou/product/xiaodou/XDConnectService.kt

@@ -25,9 +25,9 @@ import androidx.core.app.NotificationChannelCompat
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import androidx.core.content.ContextCompat
-import com.luojigou.product.xiaodou.ble.XDBLEUtils
-import com.luojigou.product.xiaodou.ble.XDScanBLEDevice
-import com.luojigou.product.xiaodou.wifi.XDScanWifiInfo
+import com.luojigou.product.ble.XDBLEUtils
+import com.luojigou.product.ble.XDScanBLEDevice
+import com.luojigou.product.wifi.XDScanWifiInfo
 import java.util.UUID
 import kotlin.concurrent.thread
 

+ 2 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/XDIntroduceActivity.kt

@@ -1,7 +1,8 @@
 package com.luojigou.product.xiaodou
 
 import android.content.Intent
-import com.luojigou.product.base.AbsIntroduceActivity
+import com.luojigou.product.intro.AbsIntroduceActivity
+import com.luojigou.product.ble.XDScanBLEActivity
 
 class XDIntroduceActivity : AbsIntroduceActivity() {
     override fun startActivity() {

+ 0 - 7
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDBLEController.kt

@@ -1,7 +0,0 @@
-package com.luojigou.product.xiaodou.ble
-
-import android.content.Context
-
-class XDBLEController(private val context: Context) {
-
-}

+ 0 - 55
xiaodou/src/main/java/com/luojigou/product/xiaodou/ble/XDBLEUtils.kt

@@ -1,55 +0,0 @@
-package com.luojigou.product.xiaodou.ble
-
-import android.util.Log
-
-object XDBLEUtils {
-    const val CONNECT_SUCCESS = 0
-    const val CONNECTING = 1
-    const val CONNECT_FAIL = 2
-    const val CONNECT_NOT_FOUND = 3
-    const val CONNECT_PWD_ERROR = 4
-
-    const val END_BYTE: Byte = 255.toByte()
-    fun parseConnectResponse(data: ByteArray): Int {
-        if (data.size < 17) return -99
-        Log.d("XDBLEUtils", "parseConnectResponse: ${data.decodeToString()}")
-        return data[16] - '0'.code
-    }
-
-    fun convertToConnectRequest(ssid: String, password: String): ByteArray {
-        val stringBuffer = StringBuffer()
-        stringBuffer.append(Char(0x7E))
-        stringBuffer.append(Char(0x01))
-        stringBuffer.append(Char(ssid.length))
-        ssid.toCharArray().forEach {
-            stringBuffer.append(it)
-        }
-        stringBuffer.append(Char(password.length))
-        password.toCharArray().forEach {
-            stringBuffer.append(it)
-        }
-        val byteArray = stringBuffer.toString().toByteArray()
-        return byteArrayOf(*byteArray, END_BYTE)
-    }
-
-    fun convert(): ByteArray {
-        val ssid = "abcd"
-        val password = "12345678"
-
-        val stringBuffer = StringBuffer()
-
-        stringBuffer.append(Char(0x7E))
-        stringBuffer.append(Char(0x01))
-        stringBuffer.append(Char(ssid.length))
-        ssid.toCharArray().forEach {
-            stringBuffer.append(it)
-        }
-        stringBuffer.append(Char(password.length))
-        password.toCharArray().forEach {
-            stringBuffer.append(it)
-        }
-        stringBuffer.append(0xFF)
-        val byteArray = stringBuffer.toString().toByteArray()
-        return byteArray
-    }
-}

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/connect/XDConnectPasswordFragment.kt

@@ -11,7 +11,7 @@ import android.widget.TextView
 import androidx.fragment.app.Fragment
 import com.luojigou.product.R
 import com.luojigou.product.xiaodou.XDConnectService
-import com.luojigou.product.xiaodou.wifi.XDScanWifiInfo
+import com.luojigou.product.wifi.XDScanWifiInfo
 
 class XDConnectPasswordFragment(private val ssid: String) : Fragment() {
     override fun onCreateView(

+ 1 - 1
xiaodou/src/main/java/com/luojigou/product/xiaodou/connect/XDConnectPasswordSsidFragment.kt

@@ -10,7 +10,7 @@ import android.widget.EditText
 import androidx.fragment.app.Fragment
 import com.luojigou.product.R
 import com.luojigou.product.xiaodou.XDConnectService
-import com.luojigou.product.xiaodou.wifi.XDScanWifiInfo
+import com.luojigou.product.wifi.XDScanWifiInfo
 
 class XDConnectPasswordSsidFragment : Fragment() {
     override fun onCreateView(

+ 10 - 0
xiaodou/src/main/res/layout/activity_introduce.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.fragment.app.FragmentContainerView
+        android:id="@+id/fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 0 - 0
xiaodou/src/main/res-xiaodou/layout/activity_xd_introduce.xml → xiaodou/src/main/res/layout/fragment_introduce_normal.xml


+ 67 - 0
xiaodou/src/main/res/layout/fragment_introduce_permission.xml

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="8dp"
+            android:gravity="center"
+            android:text="权限申请说明"
+            android:textFontWeight="600"
+            android:textSize="14pt" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:lineHeight="18dp"
+            android:paddingHorizontal="20dp"
+            android:text="        通过手机/平板给设备配网的过程中会用到蓝牙、WiFi等功能,这些功能需要用户授权相关权限后才可正常使用,本软件保证绝对不滥用和记录您的蓝牙、WIFI信息。"
+            android:textSize="14dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="12dp"
+            android:paddingHorizontal="20dp"
+            android:text="蓝牙权限说明"
+            android:textFontWeight="500"
+            android:textSize="16dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="20dp"
+            android:text="        本功能在配网过程中需要使用蓝牙来扫描和发现设备,以及连接设备和传输数据,应用需要通过蓝牙与设备互相传递数据。" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="12dp"
+            android:paddingHorizontal="20dp"
+            android:text="WIFI权限说明"
+            android:textFontWeight="500"
+            android:textSize="16dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="20dp"
+            android:text="        本功能在配网过程中需要使用WIFI来扫描和发现附近的可用网络,应用需要选择可用网络来给设备配置可用的WIFI。" />
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/btn_intro_request"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:text="继续并授予权限" />
+</LinearLayout>

+ 74 - 0
xiaodou/src/main/res/layout/fragment_introduce_permission_manual.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="8dp"
+            android:gravity="center"
+            android:text="权限申请说明"
+            android:textColor="#FF666666"
+            android:textFontWeight="600"
+            android:textSize="22dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:lineHeight="18dp"
+            android:paddingHorizontal="20dp"
+
+            android:text="        通过手机/平板给设备配网的过程中会用到蓝牙、WiFi等功能,这些功能需要用户授权相关权限后才可正常使用,本软件保证绝对不滥用和记录您的蓝牙、WIFI信息。"
+            android:textColor="#FF999999"
+            android:textSize="14dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="12dp"
+            android:paddingHorizontal="20dp"
+            android:text="蓝牙权限说明"
+            android:textColor="#FF333333"
+            android:textFontWeight="500"
+            android:textSize="16dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="20dp"
+            android:text="        本功能在配网过程中需要使用蓝牙来扫描和发现设备,以及连接设备和传输数据,应用需要通过蓝牙与设备互相传递数据。"
+            android:textColor="#FF999999" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginVertical="12dp"
+            android:paddingHorizontal="20dp"
+            android:text="WIFI权限说明"
+            android:textColor="#FF333333"
+            android:textFontWeight="500"
+            android:textSize="16dp" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="20dp"
+            android:text="        本功能在配网过程中需要使用WIFI来扫描和发现附近的可用网络,应用通过选择可用网络来给设备配置WIFI网络。"
+            android:textColor="#FF999999" />
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/btn_intro_request"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:text="手动去设置授予" />
+</LinearLayout>

+ 17 - 12
xiaodou/src/main/res/layout/item_product.xml

@@ -1,20 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <ImageView
-        android:id="@+id/iv_product_img"
-        android:layout_width="match_parent"
-        android:layout_height="160dp"
-        android:layout_margin="16dp" />
+    android:layout_height="64dp"
+    android:layout_marginVertical="4dp"
+    android:orientation="horizontal">
 
     <TextView
         android:id="@+id/tv_product_name"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:text="" />
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_marginStart="16dp"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:text=""
+        android:textSize="14pt" />
+
+    <ImageView
+        android:id="@+id/iv_product_img"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:scaleType="fitCenter"
+        android:layout_marginHorizontal="16dp" />
 
 </LinearLayout>