Commit 0af049d5 authored by Andreas Schildbach's avatar Andreas Schildbach
Browse files

Rudimentary bluetooth scanner.

For now, location permission must be granted manually by the user.
parent 311dce70
......@@ -32,4 +32,5 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
}
......@@ -17,6 +17,8 @@
</activity>
</application>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
</manifest>
......@@ -19,6 +19,7 @@ package com.example.card10companion
import android.bluetooth.*
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.card10companion.scanner.ScannerFragment
import java.util.*
class MainActivity : AppCompatActivity() {
......@@ -81,6 +82,14 @@ class MainActivity : AppCompatActivity() {
}
gatt = remoteDevice.connectGatt(this, false, callback)
var fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (fragment == null) {
fragment = ScannerFragment()
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit()
}
}
override fun onDestroy() {
......
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.card10companion.scanner;
data class Device(val btMac: String, val name: String?)
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.card10companion.scanner
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.card10companion.R
class DeviceViewHolder(inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder(inflater.inflate(R.layout.scanner_list_item, parent, false)) {
private var nameView: TextView? = null
private var btMacView: TextView? = null
init {
nameView = itemView.findViewById(R.id.scanner_list_item_name)
btMacView = itemView.findViewById(R.id.scanner_list_item_btmac)
}
fun bind(device: Device) {
nameView?.text = device.name
btMacView?.text = device.btMac
}
}
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.card10companion.scanner
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.card10companion.R
import kotlinx.android.synthetic.main.scanner_fragment.*
class ScannerFragment : Fragment() {
lateinit var bluetoothAdapter: BluetoothAdapter
val listAdapter = ScannerListAdapter()
val callback = object:ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
System.out.println("===== onScanResult " + result)
listAdapter.maybeAdd(Device(btMac = result.device.address, name = result.device.name))
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bluetoothAdapter.bluetoothLeScanner.startScan(callback)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.scanner_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
scanner_device_list.apply {
layoutManager = LinearLayoutManager(activity)
adapter = listAdapter
}
}
override fun onDestroy() {
bluetoothAdapter.bluetoothLeScanner.stopScan(callback)
super.onDestroy()
}
}
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.card10companion.scanner
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import java.util.*
class ScannerListAdapter() : RecyclerView.Adapter<DeviceViewHolder>() {
private val list = LinkedList<Device>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceViewHolder {
val inflater = LayoutInflater.from(parent.context)
return DeviceViewHolder(inflater, parent)
}
override fun onBindViewHolder(holder: DeviceViewHolder, position: Int) {
val device: Device = list[position]
holder.bind(device)
}
override fun getItemCount(): Int = list.size
fun maybeAdd(device: Device) {
if (!list.contains(device)) {
list.add(device)
notifyDataSetChanged()
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"/>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/scanner_device_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"/>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center_horizontal"
android:padding="16dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/scanner_list_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingLeft="16dp"
android:textStyle="bold"/>
<TextView
android:id="@+id/scanner_list_item_btmac"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingLeft="16dp"/>
</LinearLayout>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment