1. 程式人生 > 其它 >android 藍芽app(搜尋、配對)

android 藍芽app(搜尋、配對)

  功能:搜尋附近的藍芽裝置(僅限於經典藍芽)並顯示、對指定的藍芽裝置傳送配對請求、獲取已配對的藍芽裝置名稱、對指定的藍芽裝置取消配對。

  android 藍芽app(搜尋、配對) - Chi4ki - 部落格園 (cnblogs.com)

manifests:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.recyclerviewtest">
    <
uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
/> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-feature android:name="android.hardware.bluetooth_le"
android:required="true" /> <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.RecyclerviewTest"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="413dp"
        android:layout_height="370dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edx2"
        app:layout_constraintVertical_bias="0.0" />

    <Button
        android:id="@+id/btn1"
        android:layout_width="150dp"
        android:layout_height="60dp"
        android:layout_marginStart="16dp"
        android:layout_above="@id/btn3"
        android:layout_marginBottom="16dp"
        android:text="搜尋"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/recyclerview"
        app:layout_constraintVertical_bias="0.0" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="150dp"
        android:layout_height="60dp"
        android:layout_above="@id/btn4"
        android:layout_marginEnd="16dp"
        android:text="停止"
        app:layout_constraintBottom_toTopOf="@+id/btn4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/recyclerview"
        app:layout_constraintVertical_bias="0.0" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="150dp"
        android:layout_height="60dp"
        android:layout_above="@id/btn5"
        android:text="配對"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.061"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/recyclerview"
        app:layout_constraintVertical_bias="0.358" />

    <Button
        android:id="@+id/btn4"
        android:layout_width="150dp"
        android:layout_height="60dp"
        android:layout_above="@id/btn5"
        android:layout_marginStart="79dp"
        android:layout_marginEnd="16dp"
        android:text="取消配對"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toEndOf="@+id/btn3"
        app:layout_constraintTop_toBottomOf="@+id/recyclerview"
        app:layout_constraintVertical_bias="0.358" />

    <EditText
        android:id="@+id/edx1"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/edx2"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:ems="10"
        android:inputType="textPostalAddress"
        android:text="Address"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edx1" />

    <Button
        android:id="@+id/btn5"
        android:layout_width="380dp"
        android:layout_height="60dp"
        android:text="獲取匹配列表"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.516"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/recyclerview"
        app:layout_constraintVertical_bias="0.701" />

</androidx.constraintlayout.widget.ConstraintLayout>

item_list.xml:

<?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:background="@color/white"
    android:foreground="?attr/selectableItemBackground"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:padding="16dp">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_device_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:singleLine="true"
                android:text="裝置名稱"
                android:textColor="@color/black"
                android:textSize="16sp" />

            <TextView
                android:id="@+id/tv_mac_address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:ellipsize="end"
                android:singleLine="true"
                android:text="Mac地址" />
        </LinearLayout>


    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="#EEE" />
</LinearLayout>

Bluetooth.java:

package com.example.recyclerviewtest;

public class Bluetooth {
    public String name;
    public String address;

    public Bluetooth(String n,String a){
        this.name=n;
        this.address=a;
    }
    public void setBluetooth(String n,String a){
        this.name=n;
        this.address=a;
    }
    public void setName(String n){this.name=n;}
    public void setAddress(String a){this.address=a;}
    public String getName(){return(this.name);}
    public String getAddress(String a){return(this.address);}

    public String print(){
        return this.name+"\n"+this.address;
    }
}

 

MyAdapter.java:

package com.example.recyclerviewtest;

import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;

import java.security.AccessController;
import java.util.ArrayList;
import java.util.List;

class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHoder> {
    private MainActivity.OnItemClickListener onItemClickListener;

    static class MyViewHoder extends RecyclerView.ViewHolder {
        View item;
        TextView mTitleTv;
        TextView mTitleContent;

        public MyViewHoder(@NonNull View itemView) {
            super(itemView);
            item = itemView;
            mTitleTv = itemView.findViewById(R.id.tv_device_name);
            mTitleContent = itemView.findViewById(R.id.tv_mac_address);
        }
    }

    private final List<Bluetooth> mNewsList ;
    public MyAdapter(List<Bluetooth> data) {
        this.mNewsList = data;
    }

    @NonNull
    @Override
    public MyViewHoder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = View.inflate(parent.getContext(), R.layout.item_list, null);
        return new MyViewHoder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHoder holder, int position) {
        final Bluetooth c = mNewsList.get(position);
        Bluetooth news = mNewsList.get(position);
        holder.mTitleTv.setText(news.name);
        holder.mTitleContent.setText(news.address);
        holder.item.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onItemClickListener != null) {
                    onItemClickListener.onItemClick(c);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return mNewsList.size();
    }

    public void setOnItemClickListener(MainActivity.OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }
}

 

PermissionsActivity.java:

package com.example.recyclerviewtest;

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


import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;


public abstract class PermissionsActivity extends AppCompatActivity {

    private static final int PERMISSION_REQUEST_CODE = 200; 
    private static final String PACKAGE_URL_SCHEME = "package:"; 


    private PermissionsChecker mPermissionsChecker; 
    private boolean isRequireCheck;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPermissionsChecker = new PermissionsChecker(this);
    }

    protected void requestPermission(){
        isRequireCheck = true;
        if (isRequireCheck) {
            String[] permissions = getPermission();
            if (mPermissionsChecker.lacksPermissions(permissions)) {
                requestPermissions(permissions);
            } else {
             
                onPermissionRequestSuccess();
            }
        }
    }


    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE && hasAllPermissionsGranted(grantResults)) {
            isRequireCheck = true;

            onPermissionRequestSuccess();
        } else {
            isRequireCheck = false;
            onPermissionRequestFail();
        }
    }


    private void requestPermissions(String... permissions) {
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
    }

    private boolean hasAllPermissionsGranted(@NonNull int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult == PackageManager.PERMISSION_DENIED) {
                return false;
            }
        }
        return true;
    }


    protected void showMissingPermissionDialog(String tip) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("提示");
        builder.setMessage(tip);
        builder.setCancelable(false);
 
        builder.setNegativeButton("退出", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        });

        builder.setPositiveButton("去開啟", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startAppSettings();
                finish();
            }
        });

        builder.show();
    }



   
    public void startAppSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse(PACKAGE_URL_SCHEME + getPackageName()));
        startActivity(intent);
    }

    public abstract String[] getPermission();

    public abstract void onPermissionRequestSuccess();

    public abstract void onPermissionRequestFail();
}

 

PermissionsChecker.java:

package com.example.recyclerviewtest;

import android.content.Context;
import android.content.pm.PackageManager;

import androidx.core.content.ContextCompat;


public class PermissionsChecker {
    private final Context mContext;

    public PermissionsChecker(Context context) {
        mContext = context.getApplicationContext();
    }

    public boolean lacksPermissions(String... permissions) {
        for (String permission : permissions) {
            if (lacksPermission(permission)) {
                return true;
            }
        }
        return false;
    }

    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(mContext, permission) ==
                PackageManager.PERMISSION_DENIED;
    }
}

MainActivity.java:

package com.example.recyclerviewtest;

import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class MainActivity extends PermissionsActivity {
    String name, address,blu;
    Integer id;
    Button btn1,btn2,btn3,btn4,btn5;
    BluetoothAdapter mBluetoothAdapter;
    BluetoothManager bluetoothManager;
    RecyclerView mRecyclerView;
    MyAdapter mMyAdapter ;
    List<Bluetooth> myNewsList = new ArrayList<>();
    IntentFilter filter_found;
    BlueToothFoundReceiver blueToothFoundReceiver;
    List<String> list = new ArrayList<String>();
    Set<BluetoothDevice> devices;
    BluetoothDevice mSelectedDevice;
    int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecyclerView = findViewById(R.id.recyclerview);

        blueToothFoundReceiver= new BlueToothFoundReceiver();
        filter_found= new IntentFilter(BluetoothDevice.ACTION_FOUND);
        filter_found.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter_found.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(blueToothFoundReceiver, filter_found);

        mMyAdapter = new MyAdapter(myNewsList);
        mRecyclerView.setAdapter(mMyAdapter);
        LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
        mRecyclerView.setLayoutManager(layoutManager);

        btn1=findViewById(R.id.btn1);
        btn1.setOnClickListener(new BtnClickListener());
        btn2=findViewById(R.id.btn2);
        btn2.setOnClickListener(new BtnClickListener());
        btn3=findViewById(R.id.btn3);
        btn3.setOnClickListener(new BtnClickListener());
        btn4=findViewById(R.id.btn4);
        btn4.setOnClickListener(new BtnClickListener());
        btn5=findViewById(R.id.btn5);
        btn5.setOnClickListener(new BtnClickListener());

        mMyAdapter.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(Bluetooth c) {
                EditText textName=findViewById(R.id.edx1);
                EditText textAddress=findViewById(R.id.edx2);
                textName.setText(c.name);
                textAddress.setText(c.address);
                Toast.makeText(getApplicationContext(), "Click " + c.print(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    class BtnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            id=v.getId();
            count=0;
            if(id.equals(R.id.btn1)){
                runOnUiThread(()->myNewsList.clear());
                checkBleDevice();
                if (mBluetoothAdapter.isDiscovering()) {
                    mBluetoothAdapter.cancelDiscovery();
                }
                mBluetoothAdapter.startDiscovery();
            }
            if(id.equals(R.id.btn2)){
                checkBleDevice();
                if (mBluetoothAdapter.isDiscovering()) {
                    mBluetoothAdapter.cancelDiscovery();
                }
            }
            if(id.equals(R.id.btn3)){
                count=1;
                checkBleDevice();
                if (mBluetoothAdapter.isDiscovering()) {
                    mBluetoothAdapter.cancelDiscovery();
                }
                mBluetoothAdapter.startDiscovery();
            }
            if(id.equals(R.id.btn4)){
                checkBleDevice();
                blueToothConnectGet();
                getTargetBLEDevice();
            }
            if(id.equals(R.id.btn5)){
                runOnUiThread(()->myNewsList.clear());
                checkBleDevice();
                blueToothConnectGet();
                for (BluetoothDevice bluetoothDevice : devices) {
                    Bluetooth blue=new Bluetooth(bluetoothDevice.getName(),bluetoothDevice.getAddress());
                    myNewsList.add(blue);
                    mMyAdapter.notifyDataSetChanged();
                }
            }
        }
    }



    @Override
    public String[] getPermission() {
        return new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN};
    }
    @Override
    public void onPermissionRequestSuccess() {

    }
    @Override
    protected void onResume() {
        super.onResume();
        requestPermission();
    }
    @Override
    public void onPermissionRequestFail() {

        showMissingPermissionDialog("缺失許可權!");
    }

    @SuppressLint("HardwareIds")
    private void checkBleDevice() {
        bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (bluetoothManager != null) {
            mBluetoothAdapter = bluetoothManager.getAdapter();
            if (mBluetoothAdapter != null) {
                if (!mBluetoothAdapter.isEnabled()) {
                    if (!mBluetoothAdapter.enable()) {
                        Log.i("tag", "藍芽開啟失敗");
                    } else {
                        Log.i("tag", "藍芽已開啟");
                    }
                }else {
                    Log.i("tag","同意申請");
                }
            }
        }
    }
    public class BlueToothFoundReceiver extends BroadcastReceiver {
        @SuppressLint("NotifyDataSetChanged")
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.
                        permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED){
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 200);


                    if(count==1){
                        String a_device=device.getName()+device.getAddress();
                        if (a_device.equals(getDevice())){
                            createOrRemoveBond(1,device);
                            Log.d("tag", "匹配中");
                        }
                    } else{
                        Log.d("tag", "掃描到了:" + device.getName() + ":" + device.getAddress() + "\n");
                        Log.d("tag", String.valueOf(device));

                        name = device.getName();
                        address = device.getAddress();
                        blu = name + ":" + address;
                        for (String bluetooth : list) {
                            if (blu.equals(bluetooth)) {
                                return;
                            }
                        }

                        list.add(device.getName() + ":" + device.getAddress());
                        if (device.getName() == null) name = "Unknown";
                        myNewsList.add(new Bluetooth(name, address));
                        mMyAdapter.notifyDataSetChanged();
                    }
                }
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
                Log.e("tag","搜尋開啟");
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                Log.e("tag","搜尋完成");
            }
        }
    }

    private void createOrRemoveBond(int type, BluetoothDevice device) {
        Method method = null;
        try {
            switch (type) {
                case 1:
                    method = BluetoothDevice.class.getMethod("createBond");
                    method.invoke(device);
                    break;
                case 2:
                    method = BluetoothDevice.class.getMethod("removeBond");
                    method.invoke(device);
                    break;
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    public interface OnItemClickListener {
        void onItemClick(Bluetooth c);
    }
    private String getDevice() {
        EditText textName=findViewById(R.id.edx1);
        EditText textAddress=findViewById(R.id.edx2);
        String a_name=textName.getText().toString().trim();
        String a_address=textAddress.getText().toString().trim();
        return a_name+a_address;
    }

    public void blueToothConnectGet() {
        name = mBluetoothAdapter.getName();
        address = mBluetoothAdapter.getAddress();
        devices = mBluetoothAdapter.getBondedDevices();
        for (BluetoothDevice bluetoothDevice : devices) {
            Log.d("markTest", "配對列表:" + bluetoothDevice.getName());
        }
    }
    private void getTargetBLEDevice() {
        EditText textName=findViewById(R.id.edx1);
        EditText textAddress=findViewById(R.id.edx2);
        if (devices != null && devices.size() > 0) {
            for (BluetoothDevice bluetoothDevice : devices) {
                String a_name=textName.getText().toString().trim();
                if (bluetoothDevice != null && bluetoothDevice.getName().equalsIgnoreCase(a_name)) {
                    Log.d("tag","取消匹配");
                    mSelectedDevice = bluetoothDevice;
                    createOrRemoveBond(2,mSelectedDevice);
                    break;
                }
            }
        }
    }

}

  

 

  每一部分的實現參考上一篇部落格。

  效果: