美文网首页
Android 蓝牙打印

Android 蓝牙打印

作者: 因为我的心 | 来源:发表于2024-01-18 10:42 被阅读0次

    1、蓝牙打印的项目

    2、Android 12 蓝牙权限(解决闪退问题)

    项目配置是compileSdkVersion31 targetSdkVersion30 手机升级了Android 12后运行Crash。照理说targetSdkVersion没有用31,怎么会报Android 12的蓝牙权限错误,百思不得其解,先来看看Android12的蓝牙权限。

    1.Manifest权限

    <manifest>   
    
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    
    <!--请求旧设备上的蓝牙权限,可设置android:maxSdkVersion为30。这个兼容性步骤帮助系统只授予你的应用程序所需的蓝牙权限,当安装在运行Android 12或更高的设备上。>   
    
    <uses-permission android:name="android.permission.BLUETOOTH"                    android:maxSdkVersion="30" />   
    
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"                     android:maxSdkVersion="30" />   
    
    <!-- 有蓝牙扫描功能的时候需要。 -->
    
      <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />   
    
    <!--如果你的应用程序不使用蓝牙扫描结果得出物理位置信息,可以加上usesPermissionFlags声明你的scan结果不会用于推导出物理位置。--> 
    
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"                     android:usesPermissionFlags="neverForLocation" />
    
    <!--仅当你的应用程序使设备可被蓝牙设备发现时需要。 -->   
    
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />   
    
    <!-- 只有当你的应用程序与已经配对的蓝牙设备通信时才需要。 -->   
    
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />   
    
    <!-- 只有当你的应用程序使用蓝牙扫描结果来获取物理位置时才需要。--> 
    
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />    
    
    <!--在Android 12 如果你确定你的应用程序从来没有从蓝牙扫描结果获取物理位置,则不需要这个权限。-->  
    
    <!--在Android 12以下 这个权限是必须要的-->  
    
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <!--如果你的应用程序支持一个服务,并且可以运行在Android 10 (API级别29)或Android 11,你还必须声明ACCESS_BACKGROUND_LOCATION权限来发现蓝牙设备。-->
    
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
    </manifest>
    

    2.动态请求相关蓝牙权限

    BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT和BLUETOOTH_SCAN权限是运行时权限。因此必须在应用程序中明确请求用户批准,才能查找蓝牙设备,使设备可被其他设备发现,或与已经配对的蓝牙设备通信。当你的应用程序请求这些权限中的至少一个,系统提示用户允许你的应用程序访问附近的设备,如图1所示。

    图片.png

    //所以请加上这段代码

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    
        if (!checkSinglePermission(Manifest.permission.BLUETOOTH_SCAN) ||!checkSinglePermission(Manifest.permission.BLUETOOTH_CONNECT)) {
    
            String[] list =new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};
    
            requestPermissions(list, Constants.PERMISSION_REQUEST_SCAN);
    
        }
    
    }
    

    3.为什么闪退

    图片.png

    先完成1,2两步, 然后再去做各种蓝牙有关的操作,不然在Android 12 手机上会直接闪退!

    图片.png

    3、Android sdk版本过高解决方法

        <!--蓝牙权限-->
        <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
        <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
        <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
        <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    
    

    4、PrintUtils

    package com.asura.android.tmspda.util.print
    
    import android.Manifest
    import android.app.Activity
    import android.bluetooth.BluetoothAdapter
    import android.bluetooth.BluetoothDevice
    import android.content.BroadcastReceiver
    import android.content.Context
    import android.content.Intent
    import android.content.IntentFilter
    import android.content.pm.PackageManager
    import android.os.Build
    import android.text.TextUtils
    import android.util.Log
    import androidx.core.app.ActivityCompat
    
    import com.github.dfqin.grantor.PermissionListener
    import com.github.dfqin.grantor.PermissionsUtil
    import com.kana.crazytv.app.util.LiveDataKeyUtils
    import com.lyy.bluetoothdemo.tools.BluetoothBean
    import com.lyy.bluetoothdemo.tools.CommonDialogUtil
    import com.lyy.bluetoothdemo.tools.LiveDataBus
    import com.lyy.bluetoothdemo.tools.ToastUtils
    
    
    /**
     * 打印工具
     */
    object PrintUtils {
        var bluetoothDataList = arrayListOf<BluetoothBean>()
        var bluetoothDataAddress = arrayListOf<String>()
        var bluetoothAdapter: BluetoothAdapter? = null
        //来源,是设置页面,不需要弹窗了
        var currentSource = 0
    
        /**
         * 获取蓝牙权限
         * source: 0是不处理;1是设置页面
         */
        fun openBluetoothPermissions(context: Activity,source:Int=0) {
            currentSource = source
            bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
            //获取权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                PermissionsUtil.requestPermission(
                    context,
                    object : PermissionListener {
                        override fun permissionGranted(permission: Array<out String>) {
                            //蓝牙列表
                            bluetoothList(context)
                        }
    
                        override fun permissionDenied(permission: Array<out String>) {
                            ToastUtils.showToast(context, "用户拒绝了蓝牙权限")
                            PermissionsUtil.gotoSetting(context)
                        }
                    },
                    Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT,
                )
    
            } else {
                //蓝牙列表
                bluetoothList(context)
            }
        }
    
        /**
         * 蓝牙列表
         */
        fun bluetoothList(context: Activity) {
            if (bluetoothAdapter != null) {
                if (bluetoothAdapter!!.isEnabled) {
                    // 蓝牙已开启
                  //  LogUtils.debugInfo("蓝牙已开启")
                    //获取蓝牙列表
                    openBluetooth(context)
                } else {
                    // 蓝牙未开启
                    ToastUtils.showToast(context, "请打开蓝牙")
                    val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
                    if (ActivityCompat.checkSelfPermission(
                            context,
                            Manifest.permission.BLUETOOTH_CONNECT
                        ) == PackageManager.PERMISSION_GRANTED
                    ) {
    
                    } else {
                        context.startActivityForResult(enableBtIntent, 200)
                    }
                }
            } else {
                // 设备不支持蓝牙
                ToastUtils.showToast(context, "设备不支持蓝牙")
            }
        }
    
        /**
         * 获取蓝牙列表
         */
         fun openBluetooth(context: Context) {
            if (ActivityCompat.checkSelfPermission(
                    context,
                    Manifest.permission.BLUETOOTH_CONNECT
                ) == PackageManager.PERMISSION_GRANTED
            ) {
                val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
                Log.d("lyy","-----蓝牙已链接--------${pairedDevices?.size}")
                if (pairedDevices != null && pairedDevices.isNotEmpty()) {
                    bluetoothDataList.clear()
                    bluetoothDataAddress.clear()
                    if (currentSource==0){
                        CommonDialogUtil.commonBluetoothList(context)
                    }
                    for (device in pairedDevices) {
                        // 处理已配对设备(Android 13)
                        val deviceName = device.name
                        val deviceAddress = device.address
                        Log.d("lyy","Device Name: $deviceName, Device Address: $deviceAddress")
                        if (!TextUtils.isEmpty(deviceName)) {
                            var isExist :Boolean  = bluetoothDataAddress.any { it == deviceAddress }
                            if (!isExist) {
                                bluetoothDataAddress.add(deviceAddress)
                                var bluetoothBean = BluetoothBean(deviceName, deviceAddress, false)
                                bluetoothDataList.add(bluetoothBean)
                                LiveDataBus.get()
                                    .with(LiveDataKeyUtils.BLUETOOTH_REFRESH, String::class.java)
                                    .postValue("")
                            }
                        }
                    }
                }
            } else {
                val startDiscovery = bluetoothAdapter?.startDiscovery()
                Log.d("lyy","-----蓝牙未链接--------${startDiscovery}")
                registerBluetoothReceiver(context)
            }
    
        }
    
        private fun registerBluetoothReceiver(context: Context) {
            //filter注册广播接收器
            var filter = IntentFilter();
            //蓝牙当前状态
            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
            //开始扫描蓝牙设备广播
            filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
            //找到蓝牙设备广播
            filter.addAction(BluetoothDevice.ACTION_FOUND);
            //扫描蓝牙设备结束广播
            filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
            //蓝牙设备配对状态改变广播
            filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
            //设备扫描模式改变广播
            filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
            context.registerReceiver(broadcastReceiver, filter);
        }
    
        private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                val action = intent.action
                Log.d("lyy","-----action--------${action}")
                //开始查找设备
                if (BluetoothAdapter.ACTION_DISCOVERY_STARTED == action) {
                    //找到蓝牙设备
                    bluetoothDataList.clear()
                    bluetoothDataAddress.clear()
                    Log.d("lyy","开始查找...")
                    if (currentSource==0){
                        CommonDialogUtil.commonBluetoothList(context)
                    }
                } else if (BluetoothDevice.ACTION_FOUND == action) {
                    //搜到蓝牙设备
                    val device =
                        intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
                    //把搜索到的设备添加到已找到列表中,显示它的信息
                    if (ActivityCompat.checkSelfPermission(
                            context,
                            Manifest.permission.BLUETOOTH_CONNECT
                        ) != PackageManager.PERMISSION_GRANTED
                    ) {
                    }
                    var deviceName = device?.name ?: ""
                    var address = device?.address ?: ""
                    Log.d("lyy","设备名:${deviceName} 地址:${address}")
                    if (!TextUtils.isEmpty(deviceName)) {
                      var isExist :Boolean  = bluetoothDataAddress.any { it == address }
                      if (!isExist) {
                          bluetoothDataAddress.add(address)
                          var bluetoothBean = BluetoothBean(deviceName, address, false)
                          bluetoothDataList.add(bluetoothBean)
                          LiveDataBus.get().with(LiveDataKeyUtils.BLUETOOTH_REFRESH, String::class.java)
                              .postValue("")
                      }
                    }
                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) {
                    //搜索完毕
                    Log.d("lyy","查找结束...")
                }
            }
        }
    
    
    }
    
    

    5、PrintDataUtils

    模版.png
    package com.lyy.bluetoothdemo.tools
    
    import android.app.Activity
    import android.graphics.Bitmap
    import android.text.TextUtils
    import com.asura.android.tmspda.util.print.PrintUtils
    import com.google.zxing.WriterException
    import com.kana.crazytv.app.util.MmkvConstantUtils
    import com.lyy.bluetoothdemo.databinding.LayoutPrintR8LabelBinding
    import com.lyy.bluetoothdemo.tools.*
    import zp_print_bigdataSDK.zpPrinter
    
    object PrintDataUtils {
        /**
         * 打印模版
         */
        fun printTemplate1(context: Activity) {
            try {
                var address = MmkvUtils.decodeString(MmkvConstantUtils.LAST_BLUETOOTH_ADDRESS)
               // var address = "C0:40:56:24:98:96"
    //            var address = "123456"
                if (TextUtils.isEmpty(address)){
                    PrintUtils.openBluetoothPermissions(context)
                }else{
                    val zpSDK = zpPrinter(context)
                    if (!zpSDK.connect(address)) {
                        ToastUtils.showToast(context,"蓝牙连接失败,请检查设备或重启")
                        PrintUtils.openBluetoothPermissions(context)
                    } else {
    
                        val binding: LayoutPrintR8LabelBinding = LayoutPrintR8LabelBinding.inflate(context.layoutInflater)
                        if (!TextUtils.isEmpty("123456789")) {
                            try {
                                binding.icQrCode.setImageBitmap(CodeCreateUtil.CreateTwoDCode("123456789")) //receiptBean.getBoxCode()
                            } catch (e: WriterException) {
                                ToastUtils.showToast(context, "${e.message}")
                            }
                        }
                        binding.tvTag1.setText("RG") //RG
                        binding.tvTag2.setText("佛山仓") //订单发货
                        binding.tvTag3.setText("哈哈哈") //仓库
                        if (true) {
                            //是企拍
                            binding.tvTag4.setText("企拍")
                            //是否显示地址
                            binding.tvAddress.setText("提货地址:上海市-闵行区-剑川路-268号") //proxyAddress
                        } else {
                            //不是企拍
                            binding.tvTag4.setText("")
                            binding.tvAddress.setText("")
                        }
                        binding.tvBoxCode.setText("箱码:12345667")
                        binding.tvRelatedNumber.setText("单号:1234567788") //orderNo
                        binding.tvSupplierCode.setText("编号:1234567788") //getStoreCode
                        binding.tvShop2.setText("dhhdhd") //
                        binding.tvReceiptUser.setText("成都五金店") //firstCell
                        binding.tvOrderTime.setText("2024/01/17 12:01:21") //secondCell,不要加下单时间
    
                        //tv_receipt_address
                        binding.tvReceiptAddress.setText("无锡仓") //targetStation
                        binding.tvFlag.setText("2" + "-" + "6")
                        binding.tvReceiptFlag.setText("H890") //集货位collectAreaCode
    
                        //是BDT,文字要变小
                        binding.tvReceiptAddress.setTextSize(ImageUtil.px2dp(context,40f).toFloat())
                        binding.tvReceiptFlag.setTextSize(ImageUtil.px2dp(context,40f).toFloat())
    
                        //布局不旋转(竖向时使用)
                        //Bitmap bitmap = ViewToImageUtil.viewToBitMapFor100x80mm(binding.getRoot());
                        //布局旋转90度(横向时使用)
                        val bitmap: Bitmap =
                            ViewToImageUtil.rotateBitmap(ViewToImageUtil.viewToBitMapFor870x500(binding.getRoot()))
                        ToastUtils.showToast(context,"蓝牙连接成功")
                        zpSDK.Draw_Page_Bitmap_(bitmap, 0)
                        //是否走纸
                        var paperFeed = MmkvUtils.decodeBoolean(MmkvConstantUtils.PAPER_FEED)
                        if (paperFeed) {
                            zpSDK.Write(byteArrayOf(0x1d, 0x0c))
                        }
                        zpSDK.printerStatus()
                        zpSDK.disconnect()
                    }
                }
    
            } catch (e: Exception) {
                e.printStackTrace()
                ToastUtils.showToast(context,"${e.message}")
                PrintUtils.openBluetoothPermissions(context)
            }
        }
    }
    

    6、高版本手机蓝牙打印的问题

    • 1、用户需要手动打开蓝牙开关;
    • 2、用户还要点击设置->应用设置->应用管理->找到项目(BluetoothDemo)->点击权限管理->开启蓝牙。
    19036e8729fa1f095f532beec4b0fc6.png

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

          本文标题:Android 蓝牙打印

          本文链接:https://www.haomeiwen.com/subject/eqgrodtx.html