BluetoothMonitor || #12

MainActivity

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.neco_desarrollo.btmonitor.adapter.BtConsts;
import com.neco_desarrollo.btmonitor.bluetooth.BtConnection;

public class MainActivity extends AppCompatActivity {

private MenuItem menuItem;
private BluetoothAdapter btAdapter;
private final int ENABLE_REQUEST = 15;
private SharedPreferences pref;
private BtConnection btConnection;
private Button bA, bB;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bA = findViewById(R.id.buttonA);
bB = findViewById(R.id.buttonB);
init();
bA.setOnClickListener(v -> {
btConnection.sendMessage("A");
});
bB.setOnClickListener(v -> {
btConnection.sendMessage("B");
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.main_menu, menu);
menuItem = menu.findItem(R.id.id_bt_button);
setBtIcon();

return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {

if(item.getItemId() == R.id.id_bt_button){
if(!btAdapter.isEnabled()){
enableBt();
} else {
btAdapter.disable();
menuItem.setIcon(R.drawable.ic_bt_enable);
}

} else if(item.getItemId() == R.id.id_menu){
if(btAdapter.isEnabled()){
Intent i = new Intent(MainActivity.this, BtListActivity.class);
startActivity(i);
} else {
Toast.makeText(this, "Включите блютуз для перехода на этот экран!", Toast.LENGTH_SHORT).show();
}
} else if(item.getItemId() == R.id.id_connect){
btConnection.connect();
}
return super.onOptionsItemSelected(item);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if(requestCode == ENABLE_REQUEST){

if(resultCode == RESULT_OK){

setBtIcon();

}

}

}

private void setBtIcon(){

if(btAdapter.isEnabled()){

menuItem.setIcon(R.drawable.ic_bt_disable);

} else {

menuItem.setIcon(R.drawable.ic_bt_enable);

}

}

private void init(){
btAdapter = BluetoothAdapter.getDefaultAdapter();
pref = getSharedPreferences(BtConsts.MY_PREF, Context.MODE_PRIVATE);
btConnection = new BtConnection(this);
}

private void enableBt(){

Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(i, ENABLE_REQUEST);

}
}

BtListActivity

import android.Manifest;
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.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.Toast;

import com.neco_desarrollo.btmonitor.adapter.BtAdapter;
import com.neco_desarrollo.btmonitor.adapter.ListItem;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class BtListActivity extends AppCompatActivity {
private final int BT_REQUEST_PERM = 111;
private ListView listView;
private BtAdapter adapter;
private BluetoothAdapter btAdapter;
private List<ListItem> list;
private boolean isBtPermissionGranted = false;
private boolean isDiscovery = false;
private ActionBar ab;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bt_list);
getBtPermission();
init();

}

@Override
protected void onResume() {
super.onResume();
IntentFilter f1 = new IntentFilter(BluetoothDevice.ACTION_FOUND);
IntentFilter f2 = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
IntentFilter f3 = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(bReciever, f1);
registerReceiver(bReciever, f2);
registerReceiver(bReciever, f3);
}

@Override
protected void onPause() {
super.onPause();
unregisterReceiver(bReciever);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.bt_list_menu, menu);
return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {

if(item.getItemId() == android.R.id.home){

if(isDiscovery){

btAdapter.cancelDiscovery();
isDiscovery = false;
getPairedDevices();

} else {

finish();

}

} else if(item.getItemId() == R.id.id_search){
if(isDiscovery) return true;
ab.setTitle(R.string.discovering);
list.clear();
ListItem itemTitle = new ListItem();
itemTitle.setItemType(BtAdapter.TITLE_ITEM_TYPE);
list.add(itemTitle);
adapter.notifyDataSetChanged();
btAdapter.startDiscovery();
isDiscovery = true;
}

return true;
}

private void init(){

ab = getSupportActionBar();
btAdapter = BluetoothAdapter.getDefaultAdapter();
list = new ArrayList<>();
ActionBar ab = getSupportActionBar();
if(ab == null)return;
ab.setDisplayHomeAsUpEnabled(true);
listView = findViewById(R.id.listView);
adapter = new BtAdapter(this, R.layout.bt_list_item, list);
listView.setAdapter(adapter);
getPairedDevices();
onItemClickListener();
}

private void onItemClickListener(){

listView.setOnItemClickListener((parent, view, position, id) -> {
Log.d("MyLog","Item click");
ListItem item = (ListItem)parent.getItemAtPosition(position);
if(item.getItemType().equals(BtAdapter.DISCOVERY_ITEM_TYPE)) item.getBtDevice().createBond();
});

}

private void getPairedDevices(){

Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
list.clear();
for (BluetoothDevice device : pairedDevices) {
ListItem item = new ListItem();
item.setBtDevice(device);
list.add(item);
}
adapter.notifyDataSetChanged();
}

}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == BT_REQUEST_PERM){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
isBtPermissionGranted = true;
Toast.makeText(this, "Разрешение получено!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Нет разрешения на поиск блютуз устройств!!", Toast.LENGTH_SHORT).show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

}

private void getBtPermission(){
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
BT_REQUEST_PERM);
} else {
isBtPermissionGranted = true;
}
}

private final BroadcastReceiver bReciever = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if(BluetoothDevice.ACTION_FOUND.equals(intent.getAction())){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
ListItem item = new ListItem();
item.setBtDevice(device);
item.setItemType(BtAdapter.DISCOVERY_ITEM_TYPE);
list.add(item);
adapter.notifyDataSetChanged();
//Toast.makeText(context, "Found device name : " + device.getName(), Toast.LENGTH_SHORT).show();
} else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())){

isDiscovery = false;
getPairedDevices();
ab.setTitle(R.string.app_name);

} else if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device.getBondState() == BluetoothDevice.BOND_BONDED){
getPairedDevices();
}
}
}
};

}

ReceiveThread

import android.bluetooth.BluetoothSocket;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ReceiveThread extends Thread{
private BluetoothSocket socket;
private InputStream inputS;
private OutputStream outputS;
private byte[] rBuffer;

public ReceiveThread(BluetoothSocket socket){
this.socket = socket;
try{
inputS = socket.getInputStream();
} catch (IOException e){

}
try{
outputS = socket.getOutputStream();
} catch (IOException e){

}

}

@Override
public void run() {
rBuffer = new byte[2];
while(true){
try{
int size = inputS.read(rBuffer);
String message = new String(rBuffer, 0, size);
Log.d("MyLog","Message: " + message);
} catch (IOException e){
break;
}

}
}

public void sendMessage(byte[] byteArray){
try{
outputS.write(byteArray);
}catch (IOException e){

}
}
}

ConnectThread

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;

import java.io.IOException;
import java.util.UUID;


public class ConnectThread extends Thread {
private BluetoothAdapter btAdapter;
private BluetoothSocket mSocket;
private ReceiveThread rThread;
public static final String UUID = "00001101-0000-1000-8000-00805F9B34FB";

public ConnectThread(BluetoothAdapter btAdapter, BluetoothDevice device){
this.btAdapter = btAdapter;
try{
mSocket = device.createRfcommSocketToServiceRecord(java.util.UUID.fromString(UUID));
} catch (IOException e){

}
}

@Override
public void run() {
btAdapter.cancelDiscovery();
try{
mSocket.connect();
rThread = new ReceiveThread(mSocket);
rThread.start();
Log.d("MyLog","Connected");
} catch (IOException e){
Log.d("MyLog","Not Connected");
closeConnection();
}
}
public void closeConnection(){
try{
mSocket.close();
} catch (IOException y){

}
}

public ReceiveThread getRThread() {
return rThread;
}
}

BtConnection

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.SharedPreferences;

import com.neco_desarrollo.btmonitor.adapter.BtConsts;

public class BtConnection {
private Context context;
private SharedPreferences pref;
private BluetoothAdapter btAdapter;
private BluetoothDevice device;
private ConnectThread connectThread;

public BtConnection(Context context) {
this.context = context;
pref = context.getSharedPreferences(BtConsts.MY_PREF, Context.MODE_PRIVATE);
btAdapter = BluetoothAdapter.getDefaultAdapter();
}

public void connect(){
String mac = pref.getString(BtConsts.MAC_KEY, "");
if(!btAdapter.isEnabled() || mac.isEmpty()) return;
device = btAdapter.getRemoteDevice(mac);
if(device == null) return;
connectThread = new ConnectThread(btAdapter, device);
connectThread.start();
}

public void sendMessage(String message){
connectThread.getRThread().sendMessage(message.getBytes());
}

}

BtAdapter

import android.content.Context;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.neco_desarrollo.btmonitor.R;

import java.util.ArrayList;
import java.util.List;

public class BtAdapter extends ArrayAdapter<ListItem> {
public static final String DEF_ITEM_TYPE = "normal";
public static final String TITLE_ITEM_TYPE = "title";
public static final String DISCOVERY_ITEM_TYPE = "discovery";
private List<ListItem> mainList;
private List<ViewHolder> listViewHolders;
private SharedPreferences pref;
private boolean isDiscoveryType = false;


public BtAdapter(@NonNull Context context, int resource, List<ListItem> btList ) {
super(context, resource, btList);
mainList = btList;
listViewHolders = new ArrayList<>();
pref = context.getSharedPreferences(BtConsts.MY_PREF, Context.MODE_PRIVATE);
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
switch (mainList.get(position).getItemType()){
case TITLE_ITEM_TYPE: convertView = titleItem(convertView, parent);
break;
default: convertView = defaultItem(convertView, position, parent);
break;
}
return convertView;
}

private void savePref(int pos){
SharedPreferences.Editor editor = pref.edit();
editor.putString(BtConsts.MAC_KEY, mainList.get(pos).getBtDevice().getAddress());
editor.apply();
}

static class ViewHolder{

TextView tvBtName;
CheckBox chBtSelected;
}

private View defaultItem(View convertView, int position, ViewGroup parent){
ViewHolder viewHolder;
boolean hasViewHolder = false;
if(convertView != null) hasViewHolder = (convertView.getTag() instanceof ViewHolder);
if(convertView == null || !hasViewHolder){
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.bt_list_item, null, false);
viewHolder.tvBtName = convertView.findViewById(R.id.tvBtName);
viewHolder.chBtSelected = convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
listViewHolders.add(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
viewHolder.chBtSelected.setChecked(false);
}
if(mainList.get(position).getItemType().equals(BtAdapter.DISCOVERY_ITEM_TYPE)){
viewHolder.chBtSelected.setVisibility(View.GONE);
isDiscoveryType = true;
} else {
viewHolder.chBtSelected.setVisibility(View.VISIBLE);
isDiscoveryType = false;
}
viewHolder.tvBtName.setText(mainList.get(position).getBtDevice().getName());
viewHolder.chBtSelected.setOnClickListener(v -> {
if(!isDiscoveryType){
for(ViewHolder holder : listViewHolders){
holder.chBtSelected.setChecked(false);
}
viewHolder.chBtSelected.setChecked(true);
savePref(position);
}
});
if(pref.getString(BtConsts.MAC_KEY,"no bt selected").equals(mainList.get(position)
.getBtDevice().getAddress()))viewHolder.chBtSelected.setChecked(true);
isDiscoveryType = false;
return convertView;
}
private View titleItem(View convertView,ViewGroup parent){
boolean hasViewHolder = false;
if(convertView != null) hasViewHolder = (convertView.getTag() instanceof ViewHolder);
if(convertView == null || hasViewHolder){
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.bt_list_item_title, null, false);
}
return convertView;
}
}

BtConst

public class BtConsts {
public static final String MY_PREF = "my_pref";
public static final String MAC_KEY = "mac_key";
}

ListItem

import android.bluetooth.BluetoothDevice;

public class ListItem {

private BluetoothDevice btDevice;
private String itemType = BtAdapter.DEF_ITEM_TYPE;

public BluetoothDevice getBtDevice() {
return btDevice;
}

public void setBtDevice(BluetoothDevice btDevice) {
this.btDevice = btDevice;
}

public String getItemType() {
return itemType;
}

public void setItemType(String itemType) {
this.itemType = itemType;
}
}

activity_bt_list.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"
tools:context=".BtListActivity">

<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

activity_maint.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"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.233" />

<Button
android:id="@+id/buttonA"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<Button
android:id="@+id/buttonB"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="B"
app:layout_constraintBottom_toTopOf="@+id/buttonA"
app:layout_constraintEnd_toEndOf="@+id/buttonA"
app:layout_constraintStart_toStartOf="@+id/buttonA" />

</androidx.constraintlayout.widget.ConstraintLayout>

bt_list_item.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:focusable="false"
android:descendantFocusability="blocksDescendants"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/tvBtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="TextView"
android:textColor="@color/black"
app:layout_constraintBottom_toBottomOf="@+id/checkBox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/checkBox" />

<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

bt_list_item_title.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="wrap_content"
android:layout_margin="5dp"
android:background="@android:drawable/edit_text">

<TextView
android:id="@+id/tvBtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="@string/title_item_name"
android:textColor="@color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

bt_list_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="Search"
android:icon="@drawable/ic_search"
android:id="@+id/id_search"
android:orderInCategory="100"
app:showAsAction="ifRoom"
/>
</menu>

main_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="Connect"
android:icon="@drawable/ic_connection"
android:id="@+id/id_connect"
android:orderInCategory="100"
app:showAsAction="ifRoom"
/>

<item android:title="BtEnable"
android:icon="@drawable/ic_bt_enable"
android:id="@+id/id_bt_button"
android:orderInCategory="200"
app:showAsAction="ifRoom"
/>
<item android:title="BtList"
android:icon="@drawable/ic_menu"
android:id="@+id/id_menu"
android:orderInCategory="300"
app:showAsAction="ifRoom"
/>
</menu>

strings.xml

<resources>
<string name="app_name">BtMonitor</string>
<string name="title_item_name">Найдиные устройства:</string>
<string name="discovering">Поиск…</string>
</resources>

7 комментариев для “BluetoothMonitor || #12”

  1. Код не рабочий, я тока начинаю разбираться в Jave и Android Studio, зачем давать кривой проект? Или может у меня что не так? Стоит последняя версия студии.

  2. Здравствуйте, у меня проблема: приложение с вашим кодом, почему-то передаёт код клавиши, а не букву. То есть не А, а 65. Скажите, почему так?

  3. Привет Неко, пишу сюда, потому что больше некуда. Насчет gps трекера, он не активен, я подозреваю, что это из за того что версия андроида 10 и там какие то проблемы с этим, есть какой нибудь материал для решения этой проблемы?

  4. Добрый день!
    Пошагово повторяю проект. Начиная с 5-го урока начинаются ошибки.
    На данный момент:
    Студия ругается : Cannot resolve symbol ‘ACCESS_FINE_LOCATION’.
    Хотя разрешения в манифесте даны –
    Что не так с манифестом? или чего не хватает?
    нужны ли правки в gradle&

  5. Добрый день!
    Пошагово повторяю проект. Начиная с 5-го урока начинаются ошибки.
    На данный момент:
    Студия ругается : Cannot resolve symbol ‘ACCESS_FINE_LOCATION’.
    Хотя разрешения в манифесте даны –
    Что не так с манифестом? или чего не хватает?
    нужны ли правки в gradle?

Добавить комментарий для Николай Отменить ответ

Ваш адрес email не будет опубликован.