Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor app. Update obsolete APIs, make jetpack friendly, update files #229

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions BluetoothLeChat/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
* Copyright 2020 Google LLC
* Copyright (C) 2021 The Android Open Source Project
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
* 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,
Expand Down Expand Up @@ -54,13 +54,14 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha08'
implementation 'androidx.activity:activity-ktx:1.2.0-alpha08'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.fragment:fragment-ktx:1.3.4'
implementation 'androidx.activity:activity-ktx:1.3.0-alpha08'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "androidx.navigation:navigation-fragment-ktx:2.3.0"
implementation "androidx.navigation:navigation-ui-ktx:2.3.0"
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.recyclerview:recyclerview:1.2.0'
implementation "androidx.navigation:navigation-fragment-ktx:2.3.5"
implementation "androidx.navigation:navigation-ui-ktx:2.3.5"
}
2 changes: 1 addition & 1 deletion BluetoothLeChat/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

<application
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*/
package com.example.bluetoothlechat

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.bluetoothlechat.bluetooth.ChatServer

class MainActivity : AppCompatActivity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Allows such property names as '_messages'
*/
@file:Suppress("ObjectPropertyName")
isaidamier marked this conversation as resolved.
Show resolved Hide resolved

package com.example.bluetoothlechat.bluetooth

import android.app.Application
Expand All @@ -36,6 +42,7 @@ object ChatServer {
// hold reference to app context to run the chat server
private var app: Application? = null
private lateinit var bluetoothManager: BluetoothManager

// BluetoothAdapter should never be null if the app is installed from the Play store
// since BLE is required per the <uses-feature> tag in the AndroidManifest.xml.
// If the app is installed on an emulator without bluetooth then the app will crash
Expand Down Expand Up @@ -258,7 +265,15 @@ object ChatServer {
offset: Int,
value: ByteArray?
) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value)
super.onCharacteristicWriteRequest(
device,
requestId,
characteristic,
preparedWrite,
responseNeeded,
offset,
value
)
if (characteristic.uuid == MESSAGE_UUID) {
gattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null)
val message = value?.toString(Charsets.UTF_8)
Expand All @@ -275,7 +290,10 @@ object ChatServer {
super.onConnectionStateChange(gatt, status, newState)
val isSuccess = status == BluetoothGatt.GATT_SUCCESS
val isConnected = newState == BluetoothProfile.STATE_CONNECTED
Log.d(TAG, "onConnectionStateChange: Client $gatt success: $isSuccess connected: $isConnected")
Log.d(
TAG,
"onConnectionStateChange: Client $gatt success: $isSuccess connected: $isConnected"
)
// try to send a message to the other device as a test
if (isSuccess && isConnected) {
// discover services
Expand Down Expand Up @@ -303,7 +321,7 @@ object ChatServer {
super.onStartFailure(errorCode)
// Send error state to display
val errorMessage = "Advertise failed with error: $errorCode"
Log.d(TAG, "Advertising failed")
Log.d(TAG, errorMessage)
//_viewState.value = DeviceScanViewState.Error(errorMessage)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

package com.example.bluetoothlechat.bluetooth

import java.util.*

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

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

const val REQUEST_ENABLE_BT = 1
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
Expand All @@ -31,6 +32,7 @@ import com.example.bluetoothlechat.databinding.FragmentEnableBluetoothBinding
class EnableBluetoothFragment : Fragment() {

private var _binding: FragmentEnableBluetoothBinding? = null

// This property is only valid between onCreateView and onDestroyView.
private val binding
get() = _binding!!
Expand All @@ -42,6 +44,13 @@ class EnableBluetoothFragment : Fragment() {
}
}

private val startForResultEnableBluetooth =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
ChatServer.startServer(requireActivity().application)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ChatServer.requestEnableBluetooth.observe(this, bluetoothEnableObserver)
Expand All @@ -51,32 +60,16 @@ class EnableBluetoothFragment : Fragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
_binding = FragmentEnableBluetoothBinding.inflate(inflater, container, false)

binding.errorAction.setOnClickListener {
// Prompt user to turn on Bluetooth (logic continues in onActivityResult()).
// Prompt user to turn on Bluetooth (logic continues in registerForActivityResult()).
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
startForResultEnableBluetooth.launch(enableBtIntent)
}

return binding.root
}

override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_ENABLE_BT -> {
if (resultCode == Activity.RESULT_OK) {
ChatServer.startServer(requireActivity().application)
}
super.onActivityResult(requestCode, resultCode, data)
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@ package com.example.bluetoothlechat.bluetooth
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.bluetoothlechat.R
import com.example.bluetoothlechat.databinding.FragmentLocationRequiredBinding

private const val TAG = "LocationRequiredFrag"
private const val LOCATION_REQUEST_CODE = 0

// Fragment responsible for checking if the app has the ACCESS_FINE_LOCATION permission.
// This permission is required when using the BLE APIs so the user must grant permission
// to the app before viewing the BluetoothChatFragment or DeviceListFragment
Expand All @@ -44,7 +41,7 @@ class LocationRequiredFragment : Fragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
_binding = FragmentLocationRequiredBinding.inflate(inflater, container, false)

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

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Log.d(TAG, "onRequestPermissionsResult: ")
when(requestCode) {
LOCATION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Navigate to the chat fragment
findNavController().navigate(R.id.action_start_chat)
} else {
showError()
}
private val startForResultRequestPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
findNavController().navigate(R.id.action_start_chat)
} else {
showError()
}
}
}

private fun showError() {
binding.locationErrorMessage.visibility = View.VISIBLE
Expand All @@ -99,10 +85,7 @@ class LocationRequiredFragment : Fragment() {
// Navigate to the chat fragment
findNavController().navigate(R.id.action_start_chat)
} else {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_REQUEST_CODE
)
startForResultRequestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.bluetoothlechat.bluetooth.Message
import com.example.bluetoothlechat.R
import com.example.bluetoothlechat.bluetooth.ChatServer
import com.example.bluetoothlechat.bluetooth.Message
import com.example.bluetoothlechat.databinding.FragmentBluetoothChatBinding
import com.example.bluetoothlechat.gone
import com.example.bluetoothlechat.visible
Expand All @@ -39,12 +39,13 @@ private const val TAG = "BluetoothChatFragment"
class BluetoothChatFragment : Fragment() {

private var _binding: FragmentBluetoothChatBinding? = null

// this property is valid between onCreateView and onDestroyView.
private val binding: FragmentBluetoothChatBinding
get() = _binding!!

private val deviceConnectionObserver = Observer<DeviceConnectionState> { state ->
when(state) {
when (state) {
is DeviceConnectionState.Connected -> {
val device = state.device
Log.d(TAG, "Gatt connection observer: have device $device")
Expand All @@ -54,7 +55,6 @@ class BluetoothChatFragment : Fragment() {
showDisconnected()
}
}

}

private val connectionRequestObserver = Observer<BluetoothDevice> { device ->
Expand All @@ -77,7 +77,7 @@ class BluetoothChatFragment : Fragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
_binding = FragmentBluetoothChatBinding.inflate(inflater, container, false)

Log.d(TAG, "chatWith: set adapter $adapter")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package com.example.bluetoothlechat.chat
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.bluetoothlechat.bluetooth.Message
import com.example.bluetoothlechat.R
import com.example.bluetoothlechat.bluetooth.Message

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.bluetoothlechat.bluetooth.Message
import com.example.bluetoothlechat.R
import java.lang.IllegalArgumentException
import com.example.bluetoothlechat.bluetooth.Message

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

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
Log.d(TAG, "onBindViewHolder: ")
val message = messages[position]
when(message) {
when (val message = messages[position]) {
is Message.RemoteMessage -> {
(holder as RemoteMessageViewHolder).bind(message)
}
Expand All @@ -68,7 +66,7 @@ class MessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

override fun getItemViewType(position: Int): Int {
Log.d(TAG, "getItemViewType: ")
return when(messages[position]) {
return when (messages[position]) {
is Message.RemoteMessage -> REMOTE_MESSAGE
is Message.LocalMessage -> LOCAL_MESSAGE
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package com.example.bluetoothlechat.chat
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.bluetoothlechat.bluetooth.Message
import com.example.bluetoothlechat.R
import com.example.bluetoothlechat.bluetooth.Message

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import com.example.bluetoothlechat.scan.DeviceScanViewState.*
import com.example.bluetoothlechat.visible

private const val TAG = "DeviceScanFragment"
const val GATT_KEY = "gatt_bundle_key"

class DeviceScanFragment : Fragment() {

Expand Down Expand Up @@ -70,7 +69,7 @@ class DeviceScanFragment : Fragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
): View {
_binding = FragmentDeviceScanBinding.inflate(inflater, container, false)
val devAddr = getString(R.string.your_device_address) + ChatServer.getYourDeviceAddress()
binding.yourDeviceAddr.text = devAddr
Expand Down
Loading