Skip to content

Commit 09b1be3

Browse files
committed
refactor sample
1 parent 36c949d commit 09b1be3

22 files changed

+105
-123
lines changed

BluetoothLeChat/app/build.gradle

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2020 Google LLC
2+
* Copyright (C) 2021 The Android Open Source Project
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -54,13 +54,13 @@ android {
5454
dependencies {
5555
implementation fileTree(dir: "libs", include: ["*.jar"])
5656
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
57-
implementation 'androidx.core:core-ktx:1.3.1'
58-
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha08'
59-
implementation 'androidx.activity:activity-ktx:1.2.0-alpha08'
57+
implementation 'androidx.core:core-ktx:1.3.2'
58+
implementation 'androidx.fragment:fragment-ktx:1.3.3'
59+
implementation 'androidx.activity:activity-ktx:1.3.0-alpha07'
6060
implementation 'androidx.appcompat:appcompat:1.2.0'
61-
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
62-
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
63-
implementation 'androidx.recyclerview:recyclerview:1.1.0'
64-
implementation "androidx.navigation:navigation-fragment-ktx:2.3.0"
65-
implementation "androidx.navigation:navigation-ui-ktx:2.3.0"
61+
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
62+
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
63+
implementation 'androidx.recyclerview:recyclerview:1.2.0'
64+
implementation "androidx.navigation:navigation-fragment-ktx:2.3.5"
65+
implementation "androidx.navigation:navigation-ui-ktx:2.3.5"
6666
}

BluetoothLeChat/app/src/main/AndroidManifest.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
99

1010
<application
11-
android:allowBackup="true"
11+
android:allowBackup="false"
1212
android:icon="@mipmap/ic_launcher"
1313
android:label="@string/app_name"
1414
android:roundIcon="@mipmap/ic_launcher_round"

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/MainActivity.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package com.example.bluetoothlechat
1717

18-
import androidx.appcompat.app.AppCompatActivity
1918
import android.os.Bundle
19+
import androidx.appcompat.app.AppCompatActivity
2020
import com.example.bluetoothlechat.bluetooth.ChatServer
2121

2222
class MainActivity : AppCompatActivity() {

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/bluetooth/ChatServer.kt

+14-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ object ChatServer {
3636
// hold reference to app context to run the chat server
3737
private var app: Application? = null
3838
private lateinit var bluetoothManager: BluetoothManager
39+
3940
// BluetoothAdapter should never be null if the app is installed from the Play store
4041
// since BLE is required per the <uses-feature> tag in the AndroidManifest.xml.
4142
// If the app is installed on an emulator without bluetooth then the app will crash
@@ -248,7 +249,15 @@ object ChatServer {
248249
offset: Int,
249250
value: ByteArray?
250251
) {
251-
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value)
252+
super.onCharacteristicWriteRequest(
253+
device,
254+
requestId,
255+
characteristic,
256+
preparedWrite,
257+
responseNeeded,
258+
offset,
259+
value
260+
)
252261
if (characteristic.uuid == MESSAGE_UUID) {
253262
gattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null)
254263
val message = value?.toString(Charsets.UTF_8)
@@ -265,7 +274,10 @@ object ChatServer {
265274
super.onConnectionStateChange(gatt, status, newState)
266275
val isSuccess = status == BluetoothGatt.GATT_SUCCESS
267276
val isConnected = newState == BluetoothProfile.STATE_CONNECTED
268-
Log.d(TAG, "onConnectionStateChange: Client $gatt success: $isSuccess connected: $isConnected")
277+
Log.d(
278+
TAG,
279+
"onConnectionStateChange: Client $gatt success: $isSuccess connected: $isConnected"
280+
)
269281
// try to send a message to the other device as a test
270282
if (isSuccess && isConnected) {
271283
// discover services

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/bluetooth/Constants.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
package com.example.bluetoothlechat.bluetooth
18+
1819
import java.util.*
1920

2021
/**
@@ -25,7 +26,7 @@ import java.util.*
2526
*
2627
* Bluetooth requires a certain format for UUIDs associated with Services.
2728
* The official specification can be found here:
28-
* [://www.bluetooth.org/en-us/specification/assigned-numbers/service-discovery][https]
29+
* [https://www.bluetooth.org/en-us/specification/assigned-numbers/service-discovery]
2930
*/
3031
val SERVICE_UUID: UUID = UUID.fromString("0000b81d-0000-1000-8000-00805f9b34fb")
3132

@@ -38,5 +39,3 @@ val MESSAGE_UUID: UUID = UUID.fromString("7db3e235-3608-41f3-a03c-955fcbd2ea4b")
3839
* UUID to confirm device connection
3940
*/
4041
val CONFIRM_UUID: UUID = UUID.fromString("36d4dc5c-814b-4097-a5a6-b93b39085928")
41-
42-
const val REQUEST_ENABLE_BT = 1

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/bluetooth/EnableBluetoothFragment.kt

+12-19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import android.os.Bundle
2222
import android.view.LayoutInflater
2323
import android.view.View
2424
import android.view.ViewGroup
25+
import androidx.activity.result.contract.ActivityResultContracts
2526
import androidx.fragment.app.Fragment
2627
import androidx.lifecycle.Observer
2728
import androidx.navigation.fragment.findNavController
@@ -31,6 +32,7 @@ import com.example.bluetoothlechat.databinding.FragmentEnableBluetoothBinding
3132
class EnableBluetoothFragment : Fragment() {
3233

3334
private var _binding: FragmentEnableBluetoothBinding? = null
35+
3436
// This property is only valid between onCreateView and onDestroyView.
3537
private val binding
3638
get() = _binding!!
@@ -42,6 +44,13 @@ class EnableBluetoothFragment : Fragment() {
4244
}
4345
}
4446

47+
private val startForResultEnableBluetooth =
48+
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
49+
if (it.resultCode == Activity.RESULT_OK) {
50+
ChatServer.startServer(requireActivity().application)
51+
}
52+
}
53+
4554
override fun onCreate(savedInstanceState: Bundle?) {
4655
super.onCreate(savedInstanceState)
4756
ChatServer.requestEnableBluetooth.observe(this, bluetoothEnableObserver)
@@ -51,32 +60,16 @@ class EnableBluetoothFragment : Fragment() {
5160
inflater: LayoutInflater,
5261
container: ViewGroup?,
5362
savedInstanceState: Bundle?
54-
): View? {
63+
): View {
5564
_binding = FragmentEnableBluetoothBinding.inflate(inflater, container, false)
5665

5766
binding.errorAction.setOnClickListener {
58-
// Prompt user to turn on Bluetooth (logic continues in onActivityResult()).
67+
// Prompt user to turn on Bluetooth (logic continues in registerForActivityResult()).
5968
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
60-
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
69+
startForResultEnableBluetooth.launch(enableBtIntent)
6170
}
6271

6372
return binding.root
6473
}
6574

66-
override fun onActivityResult(
67-
requestCode: Int,
68-
resultCode: Int,
69-
data: Intent?
70-
) {
71-
super.onActivityResult(requestCode, resultCode, data)
72-
when (requestCode) {
73-
REQUEST_ENABLE_BT -> {
74-
if (resultCode == Activity.RESULT_OK) {
75-
ChatServer.startServer(requireActivity().application)
76-
}
77-
super.onActivityResult(requestCode, resultCode, data)
78-
}
79-
else -> super.onActivityResult(requestCode, resultCode, data)
80-
}
81-
}
8275
}

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/bluetooth/LocationRequiredFragment.kt

+9-26
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,16 @@ package com.example.bluetoothlechat.bluetooth
1818
import android.Manifest
1919
import android.content.pm.PackageManager
2020
import android.os.Bundle
21-
import android.util.Log
2221
import android.view.LayoutInflater
2322
import android.view.View
2423
import android.view.ViewGroup
24+
import androidx.activity.result.contract.ActivityResultContracts
2525
import androidx.core.content.ContextCompat
2626
import androidx.fragment.app.Fragment
2727
import androidx.navigation.fragment.findNavController
2828
import com.example.bluetoothlechat.R
2929
import com.example.bluetoothlechat.databinding.FragmentLocationRequiredBinding
3030

31-
private const val TAG = "LocationRequiredFrag"
32-
private const val LOCATION_REQUEST_CODE = 0
33-
3431
// Fragment responsible for checking if the app has the ACCESS_FINE_LOCATION permission.
3532
// This permission is required when using the BLE APIs so the user must grant permission
3633
// to the app before viewing the BluetoothChatFragment or DeviceListFragment
@@ -44,7 +41,7 @@ class LocationRequiredFragment : Fragment() {
4441
inflater: LayoutInflater,
4542
container: ViewGroup?,
4643
savedInstanceState: Bundle?
47-
): View? {
44+
): View {
4845
_binding = FragmentLocationRequiredBinding.inflate(inflater, container, false)
4946

5047
// hide the error messages while checking the permissions
@@ -64,25 +61,14 @@ class LocationRequiredFragment : Fragment() {
6461
checkLocationPermission()
6562
}
6663

67-
override fun onRequestPermissionsResult(
68-
requestCode: Int,
69-
permissions: Array<out String>,
70-
grantResults: IntArray
71-
) {
72-
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
73-
Log.d(TAG, "onRequestPermissionsResult: ")
74-
when(requestCode) {
75-
LOCATION_REQUEST_CODE -> {
76-
if (grantResults.isNotEmpty() &&
77-
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
78-
// Navigate to the chat fragment
79-
findNavController().navigate(R.id.action_start_chat)
80-
} else {
81-
showError()
82-
}
64+
private val startForResultRequestPermission =
65+
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
66+
if (it) {
67+
findNavController().navigate(R.id.action_start_chat)
68+
} else {
69+
showError()
8370
}
8471
}
85-
}
8672

8773
private fun showError() {
8874
binding.locationErrorMessage.visibility = View.VISIBLE
@@ -99,10 +85,7 @@ class LocationRequiredFragment : Fragment() {
9985
// Navigate to the chat fragment
10086
findNavController().navigate(R.id.action_start_chat)
10187
} else {
102-
requestPermissions(
103-
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
104-
LOCATION_REQUEST_CODE
105-
)
88+
startForResultRequestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
10689
}
10790
}
10891
}

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/chat/BluetoothChatFragment.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import androidx.fragment.app.Fragment
2727
import androidx.lifecycle.Observer
2828
import androidx.navigation.fragment.findNavController
2929
import androidx.recyclerview.widget.LinearLayoutManager
30-
import com.example.bluetoothlechat.bluetooth.Message
3130
import com.example.bluetoothlechat.R
3231
import com.example.bluetoothlechat.bluetooth.ChatServer
32+
import com.example.bluetoothlechat.bluetooth.Message
3333
import com.example.bluetoothlechat.databinding.FragmentBluetoothChatBinding
3434
import com.example.bluetoothlechat.gone
3535
import com.example.bluetoothlechat.visible
@@ -39,12 +39,13 @@ private const val TAG = "BluetoothChatFragment"
3939
class BluetoothChatFragment : Fragment() {
4040

4141
private var _binding: FragmentBluetoothChatBinding? = null
42+
4243
// this property is valid between onCreateView and onDestroyView.
4344
private val binding: FragmentBluetoothChatBinding
4445
get() = _binding!!
4546

4647
private val deviceConnectionObserver = Observer<DeviceConnectionState> { state ->
47-
when(state) {
48+
when (state) {
4849
is DeviceConnectionState.Connected -> {
4950
val device = state.device
5051
Log.d(TAG, "Gatt connection observer: have device $device")
@@ -54,7 +55,6 @@ class BluetoothChatFragment : Fragment() {
5455
showDisconnected()
5556
}
5657
}
57-
5858
}
5959

6060
private val connectionRequestObserver = Observer<BluetoothDevice> { device ->
@@ -77,7 +77,7 @@ class BluetoothChatFragment : Fragment() {
7777
inflater: LayoutInflater,
7878
container: ViewGroup?,
7979
savedInstanceState: Bundle?
80-
): View? {
80+
): View {
8181
_binding = FragmentBluetoothChatBinding.inflate(inflater, container, false)
8282

8383
Log.d(TAG, "chatWith: set adapter $adapter")

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/chat/LocalMessageViewHolder.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ package com.example.bluetoothlechat.chat
1818
import android.view.View
1919
import android.widget.TextView
2020
import androidx.recyclerview.widget.RecyclerView
21-
import com.example.bluetoothlechat.bluetooth.Message
2221
import com.example.bluetoothlechat.R
22+
import com.example.bluetoothlechat.bluetooth.Message
2323

2424
class LocalMessageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
2525

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/chat/MessageAdapter.kt

+4-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ import android.util.Log
1919
import android.view.LayoutInflater
2020
import android.view.ViewGroup
2121
import androidx.recyclerview.widget.RecyclerView
22-
import com.example.bluetoothlechat.bluetooth.Message
2322
import com.example.bluetoothlechat.R
24-
import java.lang.IllegalArgumentException
23+
import com.example.bluetoothlechat.bluetooth.Message
2524

2625
private const val TAG = "MessageAdapter"
2726
private const val REMOTE_MESSAGE = 0
@@ -33,7 +32,7 @@ class MessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
3332
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
3433
Log.d(TAG, "onCreateViewHolder: ")
3534
val inflater = LayoutInflater.from(parent.context)
36-
return when(viewType) {
35+
return when (viewType) {
3736
REMOTE_MESSAGE -> {
3837
val view = inflater.inflate(R.layout.item_remote_message, parent, false)
3938
RemoteMessageViewHolder(view)
@@ -50,8 +49,7 @@ class MessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
5049

5150
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
5251
Log.d(TAG, "onBindViewHolder: ")
53-
val message = messages[position]
54-
when(message) {
52+
when (val message = messages[position]) {
5553
is Message.RemoteMessage -> {
5654
(holder as RemoteMessageViewHolder).bind(message)
5755
}
@@ -68,7 +66,7 @@ class MessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
6866

6967
override fun getItemViewType(position: Int): Int {
7068
Log.d(TAG, "getItemViewType: ")
71-
return when(messages[position]) {
69+
return when (messages[position]) {
7270
is Message.RemoteMessage -> REMOTE_MESSAGE
7371
is Message.LocalMessage -> LOCAL_MESSAGE
7472
}

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/chat/RemoteMessageViewHolder.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ package com.example.bluetoothlechat.chat
1818
import android.view.View
1919
import android.widget.TextView
2020
import androidx.recyclerview.widget.RecyclerView
21-
import com.example.bluetoothlechat.bluetooth.Message
2221
import com.example.bluetoothlechat.R
22+
import com.example.bluetoothlechat.bluetooth.Message
2323

2424
class RemoteMessageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
2525

BluetoothLeChat/app/src/main/java/com/example/bluetoothlechat/scan/DeviceScanFragment.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,20 @@ import androidx.fragment.app.viewModels
2626
import androidx.lifecycle.Observer
2727
import androidx.navigation.fragment.findNavController
2828
import androidx.recyclerview.widget.LinearLayoutManager
29-
import com.example.bluetoothlechat.*
29+
import com.example.bluetoothlechat.R
3030
import com.example.bluetoothlechat.bluetooth.ChatServer
3131
import com.example.bluetoothlechat.databinding.FragmentDeviceScanBinding
32+
import com.example.bluetoothlechat.exhaustive
33+
import com.example.bluetoothlechat.gone
3234
import com.example.bluetoothlechat.scan.DeviceScanViewState.*
35+
import com.example.bluetoothlechat.visible
3336

3437
private const val TAG = "DeviceScanFragment"
35-
const val GATT_KEY = "gatt_bundle_key"
3638

3739
class DeviceScanFragment : Fragment() {
3840

3941
private var _binding: FragmentDeviceScanBinding? = null
42+
4043
// This property is only valid between onCreateView and onDestroyView.
4144
private val binding
4245
get() = _binding!!
@@ -66,7 +69,7 @@ class DeviceScanFragment : Fragment() {
6669
inflater: LayoutInflater,
6770
container: ViewGroup?,
6871
savedInstanceState: Bundle?
69-
): View? {
72+
): View {
7073
_binding = FragmentDeviceScanBinding.inflate(inflater, container, false)
7174

7275
binding.deviceList.apply {

0 commit comments

Comments
 (0)