看了一个视频,可以将旧手机改成蓝牙键盘进行输入,于是想试试。这一搞就搞了近一个月。本人不太会编程,只是写过个人用得着的程序,简化手中的工作。对于android开发并不懂。突击学习了一下蓝牙的知识。本来以为很简单。开始用微软的xamarin开发,毕竟微软的东西好用,而且语法熟悉。但后来发现xamarin的资料很少,最多的还是android java的。于是下了个android studio进行学习。一开始就发现grandle不好搞 ,搞 了很多天后搞定,至少可以运行demo程序了。但一直没有进展,有很多介绍客户端,服务器端的,在github上找了很多,倒有一些可以实现,但程序看不懂。本来想放弃,后来在csdn上看了几个大侠写的东西,又进行了测试,突然有一天发现测试通了。可以了。现在就将这个简单的程序给大家,有兴趣的同志可以看看。

手机做蓝牙键盘应该有很多种,我用的是最简单的。原来在ApI版本比较低的时候,有些人用了很多底层的技术,实现得非常好。但我看不懂。如github上有一个程序kontroller,在低API下可以运行,升级后无法运行。看了代码,本人水平太低,看不懂。最后实现的是用API28以后的版本,也非常简单,也是如csdn上很多大侠所说的那样,就是几个回调函数。这样将手机变成了蓝牙键盘。

这时候蓝牙既不是客户端,也不是服务器端。然后再去连接。我用的很简单,要求API31上,因为好象现在的蓝牙连接需要动态获取权限,我写的语句就是直接从网上抄下来的,成功动态给了权限。其他也不说什么,这是完整的代码,其实就是一个文件,另一个是定义HID设备的类。写的代码不规范,就是给大家个思路,再次感谢csdn上的大侠们。

package com.example.sendkeyboard;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHidDevice;
import android.bluetooth.BluetoothHidDeviceAppQosSettings;
import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.StrictMode;
import android.provider.SyncStateContract;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

    BluetoothAdapter mBluetoothAdapter;
    BluetoothHidDevice mBluetoothHID;
    public String TAG = "Proxy Program";
    boolean connected;
    private BluetoothManager mBluetoothManager;
    boolean IsRegisted;
    BluetoothDevice ConnectedDevice;
    private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            //Log.d(TAG, "Connecting HIDService...");
            if (profile == BluetoothProfile.HID_DEVICE) {

                if (!(proxy instanceof BluetoothHidDevice)) {
                    Log.e(TAG, "Proxy received but it's not BluetoothHidDevice");
                    return;
                }


                Log.d(TAG, "Connecting HIDService...");
                mBluetoothHID = (BluetoothHidDevice) proxy;
                BluetoothHidDeviceAppSdpSettings sdp = new BluetoothHidDeviceAppSdpSettings(HidConfig.NAME, HidConfig.DESCRIPTION,
                        HidConfig.PROVIDER, BluetoothHidDevice.SUBCLASS1_COMBO, HidConfig.KEYBOARD_DESCRIPTOR);
//
                if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    //    ActivityCompat#requestPermissions
                    // here to request the missing permissions, and then overriding
                    //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                    //                                          int[] grantResults)
                    // to handle the case where the user grants the permission. See the documentation
                    // for ActivityCompat#requestPermissions for more details.

                    Log.d(TAG, "Return before register");
                    String[] list = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};

                    requestPermissions(list, 1);
                    Log.d(TAG, "Return before register");
                    return;
                }

                BluetoothHidDeviceAppQosSettings inQos = new BluetoothHidDeviceAppQosSettings(
                        BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 200, 2, 200,
                        10000 /* 10 ms */, 10000 /* 10 ms */);
                BluetoothHidDeviceAppQosSettings outQos = new BluetoothHidDeviceAppQosSettings(
                        BluetoothHidDeviceAppQosSettings.SERVICE_GUARANTEED, 900, 9, 900,
                        10000 /* 10 ms */, 10000 /* 10 ms */);

                mBluetoothHID.registerApp(sdp, inQos, outQos, Executors.newCachedThreadPool(), mCallback);
                //startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE), 1);
                //https://blog.csdn.net/lgdlchshg/article/details/127469781
            }
        }

        public void onServiceDisconnected(int profile) {
            if (profile == BluetoothProfile.HID_DEVICE) {
                Log.d(TAG, "Unexpected Disconnect of HIDService...");
                mBluetoothHID = null;

                mBluetoothHID.unregisterApp();


            }


        }

    };
    public final BluetoothHidDevice.Callback mCallback = new BluetoothHidDevice.Callback() {
        @Override
        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
            IsRegisted = registered;
            if(registered){
                Log.d(TAG, "register OK!....................");
            }
        }

        @Override
        public void onConnectionStateChanged(BluetoothDevice device, int state) {
            if (state == BluetoothProfile.STATE_DISCONNECTED) {
                connected = false;
                Log.d(TAG, "hid state is disconnected");
            } else if (state == BluetoothProfile.STATE_CONNECTED) {
                connected = true;
                Log.d(TAG, "hid state is connected");
                Log.d(TAG, "----------------------------------------hid state is connected");
                Log.d(TAG, device.getName().toString());

//                byte mBuffer = (byte) 63;
//                mBluetoothHID.sendReport(device,32, mBuffer); // 不知道为啥子这样写?
                ConnectedDevice = device;
            } else if (state == BluetoothProfile.STATE_CONNECTING) {
                Log.d(TAG, "hid state is connecting");
            }
        }
    };


    public void ShowBlueToothKB() {
        mBluetoothAdapter.getProfileProxy(this, mProfileListener, BluetoothProfile.HID_DEVICE);

    }

    public void SendBKToHost() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            Log.e(TAG, "check permission Error ,Exit SendBKtohost Function");
            String[] list = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};

            requestPermissions(list, 1);

            return;
        }
//      onKeyDown((byte) 0x09);
//      onKeyUp((byte) 0x09);
        Log.e(TAG, "----------------Preparing Send Key------------------");
       // mBluetoothHID.sendReport(ConnectedDevice,2, new byte[]{0, 0,(byte)0x09, 0, 0, 0, 0, 0});
        //mBluetoothHID.sendReport(ConnectedDevice,2, new byte[]{0,0,0,0,0,0,0,0});
        // sendKey("F");
        //sendKey("_");
        sendKey("enter");
        try {
            Thread.sleep( 1000 );
        } catch (Exception e){
            System.exit( 0 ); //退出程序
        }

        sendKey("S");
        
        sendKey("enter");
//        sendKey(">");
//        sendKey("*");
        //sendKey("enter");
    }
    public void sendStringtodevice(String str)
    {
        for (int i=0;i<str.length();i++) {
            sendKey(str.substring(i,i+1));
        }

    }
    public void ConnectotherBluetooth() {

        ConnectedDevice = mBluetoothAdapter.getRemoteDevice("04:7F:0E:40:74:6E");
        if (ConnectedDevice != null) {
            Log.e(TAG, "Connected Device is OK");
            Log.e(TAG, ConnectedDevice.getName());
        }
        mBluetoothHID.connect(ConnectedDevice);//用代理去联接已联接的蓝牙设备,就能保证连接上
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        inithashMap();
        mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
       //permit network in Main UI thread
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);

        Button btnInit = findViewById(R.id.btninit);
        btnInit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //readData();
                ShowBlueToothKB();
            }


        });
        ImageView imgsend=findViewById(R.id.imgsend);
        imgsend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Log.e(TAG, "check permission");
                String[] list = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};

                requestPermissions(list, 1);

                SendBKToHost();    //readData();

            }


        });
        ImageView imgbalckscreen=findViewById(R.id.imgbalckescreen);
        imgbalckscreen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Log.e(TAG, "Send Blank screen");

                SendBBKMessage();    //readData();

            }


        });




        Button btnconnect = findViewById(R.id.btnconnect);
        btnconnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "Connect other BlueTooth");
//            String[] list =new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT};
//
//            requestPermissions(list, 3);

                ConnectotherBluetooth();


            }


        });


    }



    public void SendBBKMessage()
    {

        Log.e(TAG,"Send Message to Host");
        try
        {
            Socket socket = new Socket("127.0.0.1", 10086);//


            OutputStream outputStream = socket.getOutputStream();

            outputStream.write('s');
            outputStream.flush();

            //socket.SendUrgentData('S');
            //2.拿到客户端的socket对象的输出流发送给服务器数据
            //text1.setText("BBB");
            outputStream.close();
            socket.close();

        }
        catch (IOException e)
        {
            e.printStackTrace();
            return;







        }

    }

    private byte[] mBuffer = new byte[8];
    int id = 2;




    public void sendKey(String key) {
        byte b1 = 0;

        if (key.length() <= 1) {
            char keyChar = key.charAt(0);
            if ((keyChar >= 65) && (keyChar <= 90)) {
                b1 = 2;
            }
        }


        if (SHITBYTE.containsKey(key)) {
            b1 = 2;
        }


        mBluetoothHID.sendReport(ConnectedDevice, 2, new byte[]{b1, 0, KEY2BYTE.get(key.toUpperCase()),0,0,0,0,0});
        mBluetoothHID.sendReport(ConnectedDevice, 2,  new byte[]{0,0, 0,0,0,0,0,0});

    }
    public  Map<String,Byte> KEY2BYTE = new HashMap<String,Byte>();
    public  Map<String,Boolean> SHITBYTE = new HashMap<String,Boolean> ();

    public void inithashMap()
    {
        KEY2BYTE.put("A", (byte) 4);
        KEY2BYTE.put("B",(byte)5);
        KEY2BYTE.put("C",(byte)6);
        KEY2BYTE.put("D",(byte)7);
        KEY2BYTE.put("E",(byte)8);
        KEY2BYTE.put("F",(byte)9);
        KEY2BYTE.put("G",(byte)10);
        KEY2BYTE.put("H",(byte)11);
        KEY2BYTE.put("I",(byte)12);
        KEY2BYTE.put("J",(byte)13);
        KEY2BYTE.put("K",(byte)14);
        KEY2BYTE.put("L",(byte)15);
        KEY2BYTE.put("M",(byte)16);
        KEY2BYTE.put("N",(byte)17);
        KEY2BYTE.put("O",(byte)18);
        KEY2BYTE.put("P",(byte)19);
        KEY2BYTE.put("Q",(byte)20);
        KEY2BYTE.put("R",(byte)21);
        KEY2BYTE.put("S",(byte)22);
        KEY2BYTE.put("T",(byte)23);
        KEY2BYTE.put("U",(byte)24);
        KEY2BYTE.put("V",(byte)25);
        KEY2BYTE.put("W",(byte)26);
        KEY2BYTE.put("X",(byte)27);
        KEY2BYTE.put("Y",(byte)28);
        KEY2BYTE.put("Z",(byte)29);

        KEY2BYTE.put("1",(byte)30);
        KEY2BYTE.put("2",(byte)31);
        KEY2BYTE.put("3",(byte)32);
        KEY2BYTE.put("4",(byte)33);
        KEY2BYTE.put("5",(byte)34);
        KEY2BYTE.put("6",(byte)35);
        KEY2BYTE.put("7",(byte)36);
        KEY2BYTE.put("8",(byte)37);
        KEY2BYTE.put("9",(byte)38);
        KEY2BYTE.put("0",(byte)39);

        KEY2BYTE.put("ENTER",(byte)40);
        KEY2BYTE.put("ESC",(byte)41);
        KEY2BYTE.put("BACK_SPACE",(byte)42);
        KEY2BYTE.put("TAB",(byte)43);
        KEY2BYTE.put("SPACE",(byte)44);
        KEY2BYTE.put("-",(byte)45);
        KEY2BYTE.put("=",(byte)46);
        KEY2BYTE.put("[",(byte)47);
        KEY2BYTE.put("]",(byte)48);
        KEY2BYTE.put("\\",(byte)49);
        KEY2BYTE.put(";",(byte)51);
        KEY2BYTE.put("'",(byte)52);
        KEY2BYTE.put("`",(byte)53);
        KEY2BYTE.put(",",(byte)54);
        KEY2BYTE.put(".",(byte)55);
        KEY2BYTE.put("/",(byte)56);
        KEY2BYTE.put("SCROLL_LOCK",(byte)71);
        KEY2BYTE.put("INSERT ",(byte)73);
        KEY2BYTE.put("HOME ",(byte)74);
        KEY2BYTE.put("PAGE_UP  ",(byte)75);
        KEY2BYTE.put("DELETE ",(byte)76);
        KEY2BYTE.put("END ",(byte)77);
        KEY2BYTE.put("PAGE_DOWN ",(byte)78);
        KEY2BYTE.put("DPAD_RIGHT ",(byte)79);
        KEY2BYTE.put("KEYCODE_DPAD_LEFT ",(byte)80);
        KEY2BYTE.put("KEYCODE_DPAD_DOWN ",(byte)81);
        KEY2BYTE.put("KEYCODE_DPAD_UP ",(byte)82);
        KEY2BYTE.put("NUM_LOCK ",(byte)83);




        KEY2BYTE.put("!",(byte)30);
        SHITBYTE.put("!",true);

        KEY2BYTE.put("@",(byte)31);
        SHITBYTE.put("@",true);

        KEY2BYTE.put("#",(byte)32);
        SHITBYTE.put("#",true);

        KEY2BYTE.put("$",(byte)33);
        SHITBYTE.put("$",true);

        KEY2BYTE.put("%",(byte)34);
        SHITBYTE.put("%",true);

        KEY2BYTE.put("^",(byte)35);
        SHITBYTE.put("^",true);

        KEY2BYTE.put("&",(byte)36);
        SHITBYTE.put("&",true);

        KEY2BYTE.put("*",(byte)37);
        SHITBYTE.put("*",true);

        KEY2BYTE.put("(",(byte)38);
        SHITBYTE.put("(",true);

        KEY2BYTE.put(")",(byte)39);
        SHITBYTE.put(")",true);


        KEY2BYTE.put("_",(byte)45);
        SHITBYTE.put("_",true);

        KEY2BYTE.put("+",(byte)46);
        SHITBYTE.put("+",true);


        KEY2BYTE.put("{",(byte)47);
        SHITBYTE.put("{",true);

        KEY2BYTE.put("}",(byte)48);
        SHITBYTE.put("}",true);

        KEY2BYTE.put("|",(byte)49);
        SHITBYTE.put("|",true);

        KEY2BYTE.put(":",(byte)51);
        SHITBYTE.put(":",true);

        KEY2BYTE.put("\"",(byte)52);
        SHITBYTE.put("\"",true);

        KEY2BYTE.put("<",(byte)54);
        SHITBYTE.put("<",true);

        KEY2BYTE.put(">",(byte)55);
        SHITBYTE.put(">",true);

        KEY2BYTE.put("?",(byte)56);
        SHITBYTE.put("?",true);

    }

}

==================Hidconfig============文件

package com.example.sendkeyboard;

public class HidConfig {
    public final static String NAME = "My Keyboard";

    public final static String DESCRIPTION = " Keyboard";

    public final static String PROVIDER = "df";

    public   final static byte[] KEYBOARD_DESCRIPTOR =
            {
                    (byte) 0x05, (byte) 0x01,                    // USAGE_PAGE (Generic Desktop)
                    (byte) 0x09, (byte) 0x06,                    // USAGE (Keyboard)
                    (byte) 0xa1, (byte) 0x01,                    // COLLECTION (Application)
                    (byte) 0x85, (byte) 0x02,                    //REPORT_ID (2)
                    (byte) 0x05, (byte) 0x07,                    //   USAGE_PAGE (Keyboard)
                    (byte) 0x19, (byte) 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
                    (byte) 0x29, (byte) 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
                    (byte) 0x15, (byte) 0x00,                    //   LOGICAL_MINIMUM (0)
                    (byte) 0x25, (byte) 0x01,                    //   LOGICAL_MAXIMUM (1)
                    (byte) 0x75, (byte) 0x01,                    //   REPORT_SIZE (1)
                    (byte) 0x95, (byte) 0x08,                    //   REPORT_COUNT (8)
                    (byte) 0x81, (byte) 0x02,                    //   INPUT (Data,Var,Abs)
                    (byte) 0x95, (byte) 0x01,                    //   REPORT_COUNT (1)
                    (byte) 0x75, (byte) 0x08,                    //   REPORT_SIZE (8)
                    (byte) 0x81, (byte) 0x03,                    //   INPUT (Cnst,Var,Abs)
                    (byte) 0x95, (byte) 0x05,                    //   REPORT_COUNT (5)
                    (byte) 0x75, (byte) 0x01,                    //   REPORT_SIZE (1)
                    (byte) 0x05, (byte) 0x08,                    //   USAGE_PAGE (LEDs)
                    (byte) 0x19, (byte) 0x01,                    //   USAGE_MINIMUM (Num Lock)
                    (byte) 0x29, (byte) 0x05,                    //   USAGE_MAXIMUM (Kana)
                    (byte) 0x91, (byte) 0x02,                    //   OUTPUT (Data,Var,Abs)
                    (byte) 0x95, (byte) 0x01,                    //   REPORT_COUNT (1)
                    (byte) 0x75, (byte) 0x03,                    //   REPORT_SIZE (3)
                    (byte) 0x91, (byte) 0x03,                    //   OUTPUT (Cnst,Var,Abs)
                    (byte) 0x95, (byte) 0x06,                    //   REPORT_COUNT (6)
                    (byte) 0x75, (byte) 0x08,                    //   REPORT_SIZE (8)
                    (byte) 0x15, (byte) 0x00,                    //   LOGICAL_MINIMUM (0)
                    (byte) 0x25, (byte) 0x65,                    //   LOGICAL_MAXIMUM (101)
                    (byte) 0x05, (byte) 0x07,                    //   USAGE_PAGE (Keyboard)
                    (byte) 0x19, (byte) 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
                    (byte) 0x29, (byte) 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
                    (byte) 0x81, (byte) 0x00,                    //   INPUT (Data,Ary,Abs)
                    (byte) 0xc0,                           // END_COLLECTION
                                           // END_COLLECTION
            };
}
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐