For a few days, I'm trying to implement a BLE connection in my app. I do that in secondary Fragment rather than the main one. But when I scan to find a BLE device (with startScan(leScanCallback)), it never goes in the callback method leScanCallback. I followed some tutorial but I can't find out why it's not working.
Here is my MainActivity.kt
package com.example.start
class MainActivity : AppCompatActivity() {
private lateinit var drawerLayout: DrawerLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
drawerLayout = binding.drawerLayout
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout) //link the navigation controller & drawer layout to the app bar
NavigationUI.setupWithNavController(binding.navView, navController) //allows the user to display the navigation drawer
//PERMISSION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(baseContext,
Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
val PERMISSION_CODE = 0 //Should be >= 0
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
PERMISSION_CODE)
}
}
//==========================Bluetooth Part==========================================
val REQUEST_ENABLE_BT : Int = 1 //Will stock the result of enabling the bluetooth
//RESULT_OK = -1 (0xffffffff)
//RESULT_CANCELLED = 0 (0x00000000)
//val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
val bluetoothAdapter: BluetoothAdapter by lazy {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter
}
if (bluetoothAdapter == null) {
// Device doesn't support Bluetooth
}
if (bluetoothAdapter?.isEnabled == false) { //If bluetooth is disable, we active it
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
}
}
And here is my ConnectFragment.kt where I push the button "btnScan" to start the research
package com.example.start
private const val SELECT_DEVICE_REQUEST_CODE = 0
class ConnectFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
//return inflater.inflate(R.layout.fragment_connect, container, false)
val binding = DataBindingUtil.inflate<FragmentConnectBinding>(
inflater, R.layout.fragment_connect, container, false
)
binding.btnScan.setOnClickListener {
view : View ->
scanLeDevice()
}
return binding.root
}
private val bluetoothLeScanner: BluetoothLeScanner
get() {
val bluetoothManager = requireContext().getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter = bluetoothManager.adapter
return bluetoothAdapter.bluetoothLeScanner
}
// Device scan callback.
private val leScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
//TODO TEST TEST TEST
Log.d("ScanDeviceActivity", "leScanCallback >>")
Log.d("ScanDeviceActivity", "onScanResult(): ${result?.device?.address} - ${result?.device?.name}")
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
super.onBatchScanResults(results)
Log.d("DeviceListActivity","onBatchScanResults:${results.toString()}")
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
Log.d("DeviceListActivity", "onScanFailed: $errorCode")
}
}
private var scanning = false
private val handler = Handler()
// Stops scanning after 10 seconds.
private val SCAN_PERIOD: Long = 10000
fun scanLeDevice() {
if (!scanning) { // Stops scanning after a pre-defined scan period.
handler.postDelayed({
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}, SCAN_PERIOD)
scanning = true
//PERMISSION COARSE LOCATION
Log.d("ScanDeviceStart", "startScan()")
when (PermissionChecker.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION)) {
PackageManager.PERMISSION_GRANTED -> bluetoothLeScanner.startScan(leScanCallback)
else -> requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 1)
}
} else {
scanning = false
bluetoothLeScanner.stopScan(leScanCallback)
}
}
//Permission
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
1 -> when (grantResults) {
intArrayOf(PackageManager.PERMISSION_GRANTED) -> {
Log.d("ScanDevices", "onRequestPermissionsResult(PERMISSION_GRANTED)")
bluetoothLeScanner.startScan(leScanCallback)
}
else -> {
Log.d("ScanDevices", "onRequestPermissionsResult(not PERMISSION_GRANTED)")
}
}
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}
After I pushed my button, the debug's console ends with:
D/BluetoothLeScanner: Stop Scan with callback
Related
I have a problem with my bluetooth scanner.I'm using a samsung A50 with android 11 .So my Bluetooth is supposed to check the surrounding bluetooth devices and then add them into an ArrayList and display it on screen with a recyclerview. The problem is I don't find any device. I tried to debug my code and It seems that my code doesn't go to mReceiver that's why I don't detect any device. The code was working last week I found the devices (I found the same device multiple times it's normal) but they didn't add to the ArrayList. So Now I'm asking you guys here is my code.
I'm pretty sure I gave all the permissions necessary and even more and I also given the location permission to my app.
This is The Activity:
class ConnectionActivity : AppCompatActivity(),BluetoothOnItemClickListener{
private lateinit var binding : ActivityConnectionBinding
private lateinit var manager : RecyclerView.LayoutManager
val dataset = Datasource().loadDataBluetooth()
private val devices_list : ArrayList<BluetoothDevice> = ArrayList()
var m_bluetoothAdapter : BluetoothAdapter? = null
val REQUEST_ENABLE_BLUETOOTH = 1
companion object{
val EXTRA_ADDRESS :String = "Device_address"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityConnectionBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = "Bluetooth"
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if(m_bluetoothAdapter == null){
Toast.makeText(this, "not supported", Toast.LENGTH_SHORT).show()
return
}
if (!m_bluetoothAdapter!!.isEnabled){
val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBluetoothIntent,REQUEST_ENABLE_BLUETOOTH)
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivity(discoverableIntent)
}else{
discoverDevices()}
}
private val mReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (BluetoothDevice.ACTION_FOUND == action) {
// A Bluetooth device was found
// Getting device information from the intent
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
if (device != null) {
devices_list.add(device)
println("SIZE : ${devices_list.size}")
}
}
}
}
private fun discoverDevices(){
if (m_bluetoothAdapter!!.isDiscovering) {
// Bluetooth is already in mode discovery mode, we cancel to restart it again
m_bluetoothAdapter!!.cancelDiscovery()
}
val bool = m_bluetoothAdapter?.startDiscovery()
Log.i("", bool.toString())
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
registerReceiver(mReceiver, IntentFilter(BluetoothDevice.ACTION_FOUND))
println("Count : ${devices_list.size}")
manager = LinearLayoutManager(this)
binding.recycleView.adapter = ItemAdapter(devices_list,this)
binding.recycleView.layoutManager = manager
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
if (resultCode == Activity.RESULT_OK) {
if (m_bluetoothAdapter!!.isEnabled) {
Toast.makeText(this, "Bluetooth enabled", Toast.LENGTH_SHORT).show()
discoverDevices()
} else {
Toast.makeText(this, "Bluetooth disabled", Toast.LENGTH_SHORT).show()
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this, "Bluetooth enabling has been canceled", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.menu_connection, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.ic_ip -> Toast.makeText(this, "IP", Toast.LENGTH_SHORT).show()
}
return super.onOptionsItemSelected(item)
}
override fun onStop() {
super.onStop()
unregisterReceiver(mReceiver)
}
}
And This is the Adapter :
class ItemAdapter(private val devices : ArrayList<BluetoothDevice>, var clickListener: BluetoothOnItemClickListener): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>(){
inner class ItemViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item : BluetoothDevice,action: BluetoothOnItemClickListener){
binding.item?.StringNameB = item.name
itemView.setOnClickListener {
binding.item?.checked = true
notifyItemChanged(bindingAdapterPosition)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
val adapterlayout = ListItemBinding.inflate(inflater,parent,false)
return ItemViewHolder(adapterlayout)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(devices[position],clickListener)
holder.binding.executePendingBindings()
}
override fun getItemCount(): Int {
return devices.size
}
}
interface BluetoothOnItemClickListener{
}
And this is my the permissions I have in my manifest
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
You also need to request permissions at runtime. Have a look at the links below for more information:-
Bluetooth scanner not discovering devices
Turn on LE scanning without asking for user permission
How to request location permission at runtime
The ultimate guide to Android BLE
I am currently developing an Android NFC application. This application contains a NavigationDrawer in which I can access 3 different fragments which each correspond to 3 different NFC features.
First, I want to verify that my application can scan an NFC tag (here I have an NXP NTAG 5 boost tag, which is an NFC Forum Type 5 tag).
My problem is that when my application is running, the onNewIntent of my MainActivity is never called.
I'm sure it's not due to the hardware as I can detect this tag with common applications, but it may be due to parameters I passed to a function in the NfcManager file, like activity.applicationContext but I'm not sure.
For now, I specify that I don't want a particular activity to launch when a tag is detected, I just want to detect an NFC tag when the application runs.
Can you help me?
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private val TAG = MainActivity::class.java.simpleName
var tag: Tag? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkNFC(this)
setupNFC(this)
configureToolbar()
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun configureToolbar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == ENABLE_NFC_REQUEST_CODE) {
onActivityResultOutSourced(this)
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
override fun onResume() {
super.onResume()
onResumeOutSourced(this)
}
override fun onPause() {
super.onPause()
onPauseOutSourced(this)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
val techList = tag!!.techList
//Check that the discovered tag is a vicinity tag
if (techList[0] == "android.nfc.tech.NfcV") {
val tagUid = tag!!.id
nfcvTag = NfcV.get(tag)
//ISO/IEC 15693 tags can be operated in two modes:
// Select mode and Addressed mode.
//To work in the select mode it is needed to send a SELECT
// command at the beginning of communic.
//In the address mode, the tag UID is sent within each command.
//This application works in SELECT MODE.
val select_command: ByteArray = RFCommands.cmd_select
System.arraycopy(tagUid, 0, select_command, 2, 8)
if (nfcvTag != null) {
try {
nfcvTag!!.connect()
val select_respo = nfcvTag!!.transceive(select_command)
Log.d(TAG, "Select response: " +
Tools.byteArrayToHex(select_respo))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
companion object {
const val ENABLE_NFC_REQUEST_CODE = 0x11
private var nfcvTag: NfcV? = null
}
}
NfcManager:
class NfcManager {
companion object {
private var mNfcAdapter: NfcAdapter? = null
private var mPendingIntent: PendingIntent? = null
private lateinit var writeTagFilters: Array<IntentFilter>
private lateinit var mTechLists: Array<Array<String>>
/**
* Check the availability of NFC interface and let the user enable them
* if not active during the activity creation
*/
fun checkNFC(#NonNull activity: Activity) {
if (activity.packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(activity.applicationContext)
if (mNfcAdapter != null && !mNfcAdapter!!.isEnabled) {
AlertDialog.Builder(activity.applicationContext)
.setTitle(activity.resources.getString(R.string.dialog_nfc_not_enabled_title))
.setMessage(activity.resources.getString(R.string.dialog_nfc_not_enabled_msg))
.setPositiveButton(activity.resources.getString(R.string.dialog_nfc_not_enabled_positive_btn)
) { _, _ -> activity.startActivityForResult(Intent(Settings.ACTION_NFC_SETTINGS), MainActivity.ENABLE_NFC_REQUEST_CODE) }
.setNegativeButton(activity.resources.getString(R.string.dialog_nfc_not_enabled_negative_btn)
) { _, _ ->
Toast.makeText(activity.applicationContext, activity.resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
activity.finish()
}.show()
}
} else {
Toast.makeText(activity.applicationContext, activity.resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
activity.finish()
}
}
/**
* Create a generic PendingIntent that will be delivered to this activity.
* The NFC stack will fill in the intent with the details of the discovered
* tag before delivering it to this activity.
*/
fun setupNFC(#NonNull activity: Activity) {
mPendingIntent = PendingIntent.getActivity(activity, 0, Intent(
activity, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
writeTagFilters = arrayOf(tagDetected)
mTechLists = arrayOf(arrayOf(
NfcV::class.java.name
))
}
fun onActivityResultOutSourced(#NonNull activity: Activity) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(activity)
if (mNfcAdapter!!.isEnabled) {
Toast.makeText(activity.applicationContext, activity.resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
activity.finish()
}
}
fun onResumeOutSourced(#NonNull activity: Activity) {
if (mNfcAdapter != null) {
mNfcAdapter!!.enableForegroundDispatch(activity, mPendingIntent, writeTagFilters, mTechLists)
}
}
fun onPauseOutSourced(#NonNull activity: Activity) {
if (mNfcAdapter != null) mNfcAdapter!!.disableForegroundDispatch(activity)
}
}
}
EDIT:
The objective of the "NfcManager" class was to encapsulate as much as possible the NFC functions, in order to avoid having them in the "MainActivity".
The problem is that the previous code doesn't work, while the following code, in which the logic is contained in the "MainActivity" works well.
I really don't understand what the problem is.
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private val TAG: String = this::class.java.simpleName
private val ENABLE_NFC_REQUEST_CODE = 0x11
private var mNfcAdapter: NfcAdapter? = null
private var nfcvTag: NfcV? = null
var tag: Tag? = null
private var mPendingIntent: PendingIntent? = null
private lateinit var writeTagFilters: Array<IntentFilter>
private lateinit var mTechLists: Array<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkNFC()
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
setNfcIntent()
configureToolbar()
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun configureToolbar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
}
private fun setNfcIntent() {
// Create a generic PendingIntent that will be delivered to this activity. The NFC stack will fill
// in the intent with the details of the discovered tag before delivering it to this activity.
mPendingIntent = PendingIntent.getActivity(this, 0, Intent(
applicationContext, javaClass)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
writeTagFilters = arrayOf(tagDetected)
mTechLists = arrayOf(arrayOf(
NfcV::class.java.name
))
}
override fun onActivityResult(requestCode: Int,
resultCode: Int, data: Intent?) {
if (requestCode == ENABLE_NFC_REQUEST_CODE) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (mNfcAdapter!!.isEnabled) {
Toast.makeText(applicationContext, resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
finish()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
/**
* Check the availability of NFC and BLE interfaces and let the user enable them
* if not active during the activity creation
*/
private fun checkNFC() {
if (packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (mNfcAdapter != null && !mNfcAdapter!!.isEnabled) {
AlertDialog.Builder(this)
.setTitle(resources.getString(R.string.dialog_nfc_not_enabled_title))
.setMessage(resources.getString(R.string.dialog_nfc_not_enabled_msg))
.setPositiveButton(resources.getString(R.string.dialog_nfc_not_enabled_positive_btn)
) { dialog, which -> startActivityForResult(Intent(Settings.ACTION_NFC_SETTINGS), ENABLE_NFC_REQUEST_CODE) }
.setNegativeButton(resources.getString(R.string.dialog_nfc_not_enabled_negative_btn)
) { dialog, which ->
Toast.makeText(applicationContext, resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
finish()
}.show()
}
} else {
Toast.makeText(applicationContext, resources.getString(R.string.nfc_not_enabled), Toast.LENGTH_LONG).show()
finish()
}
}
override fun onResume() {
super.onResume()
if (mNfcAdapter != null) {
mNfcAdapter!!.enableForegroundDispatch(this, mPendingIntent, writeTagFilters, mTechLists)
}
}
override fun onPause() {
super.onPause()
if (mNfcAdapter != null) mNfcAdapter!!.disableForegroundDispatch(this)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
val techList = tag!!.techList
//Check that the discovered tag is a vicinity tag
if (techList[0] == "android.nfc.tech.NfcV") {
val tagUid = tag!!.id
nfcvTag = NfcV.get(tag)
//ISO/IEC 15693 tags can be operated in two modes:
// Select mode and Addressed mode.
//To work in the select mode it is needed to send a SELECT
// command at the beginning of communic.
//In the address mode, the tag UID is sent within each command.
//This application works in SELECT MODE.
val select_command: ByteArray = RFCommands.cmd_select
System.arraycopy(tagUid, 0, select_command, 2, 8)
if (nfcvTag != null) {
try {
nfcvTag!!.connect()
val select_respo: ByteArray = nfcvTag!!.transceive(select_command)
Log.d(TAG, "Select response: " +
Tools.byteArrayToHex(select_respo))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I am a newbie at develop android app.
I want to use "Droid Speech" but I can't use it, because "Droid Speech" is made of AppCompatActivity.
I want to how to change AppCompatActivity into Fragment.
this is my code
class DroidSpeechActivity : AppCompatActivity(), View.OnClickListener,
OnDSListener,
OnDSPermissionsListener {
val TAG = "Activity_DroidSpeech"
private var droidSpeech: DroidSpeech? = null
private var finalSpeechResult: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_droid_speech)
droidSpeech =
DroidSpeech(this, fragmentManager)
droidSpeech!!.setOnDroidSpeechListener(this)
droidSpeech!!.setShowRecognitionProgressView(false)
droidSpeech!!.setOneStepResultVerify(true)
droidSpeech!!.setRecognitionProgressMsgColor(Color.WHITE)
droidSpeech!!.setOneStepVerifyConfirmTextColor(Color.WHITE)
droidSpeech!!.setOneStepVerifyRetryTextColor(Color.WHITE)
droidSpeech!!.setPreferredLanguage("ko-KR")
finalSpeechResult = findViewById(R.id.finalSpeechResult)
start.setOnClickListener(this)
stop.setOnClickListener(this)
}
override fun onPause() {
super.onPause()
if (stop!!.visibility == View.VISIBLE) {
stop!!.performClick()
}
}
override fun onDestroy() {
super.onDestroy()
if (stop!!.visibility == View.VISIBLE) {
stop!!.performClick()
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.start -> {
// Starting droid speech
droidSpeech!!.startDroidSpeechRecognition()
// Setting the view visibilities when droid speech is running
start!!.visibility = View.GONE
stop!!.visibility = View.VISIBLE
}
R.id.stop -> {
// Closing droid speech
droidSpeech!!.closeDroidSpeechOperations()
stop!!.visibility = View.GONE
start!!.visibility = View.VISIBLE
}
}
}
// MARK: DroidSpeechListener Methods
override fun onDroidSpeechSupportedLanguages(currentSpeechLanguage: String, supportedSpeechLanguages: List<String>) {
Log.i(TAG, "Current speech language = $currentSpeechLanguage")
Log.i(TAG, "Supported speech languages = $supportedSpeechLanguages")
if (supportedSpeechLanguages.contains("ko-KR")) {
// Setting the droid speech preferred language as tamil if found
droidSpeech!!.setPreferredLanguage("ko-KR")
// Setting the confirm and retry text in tamil
droidSpeech!!.setOneStepVerifyConfirmText("check")
droidSpeech!!.setOneStepVerifyRetryText("no")
}
}
override fun onDroidSpeechRmsChanged(rmsChangedValue: Float) {
}
override fun onDroidSpeechLiveResult(liveSpeechResult: String) {
Log.i(TAG, "Live speech result = $liveSpeechResult")
}
override fun onDroidSpeechFinalResult(finalSpeechResult: String) {
this.finalSpeechResult!!.text = finalSpeechResult
println("test$finalSpeechResult")
if (droidSpeech!!.continuousSpeechRecognition) {
val colorPallets1 = intArrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA)
val colorPallets2 = intArrayOf(Color.YELLOW, Color.RED, Color.CYAN, Color.BLUE, Color.GREEN)
// Setting random color pallets to the recognition progress view
droidSpeech!!.setRecognitionProgressViewColors(if (Random().nextInt(2) == 0) colorPallets1 else colorPallets2)
} else {
stop!!.visibility = View.GONE
start!!.visibility = View.VISIBLE
}
}
override fun onDroidSpeechClosedByUser() {
stop!!.visibility = View.GONE
start!!.visibility = View.VISIBLE
}
override fun onDroidSpeechError(errorMsg: String) {
Toast.makeText(this, errorMsg, Toast.LENGTH_LONG).show()
stop!!.post {
stop!!.performClick()
}
}
// MARK: DroidSpeechPermissionsListener Method
override fun onDroidSpeechAudioPermissionStatus(audioPermissionGiven: Boolean, errorMsgIfAny: String) {
if (audioPermissionGiven) {
start!!.post {
start!!.performClick()
}
} else {
if (errorMsgIfAny != null) {
Toast.makeText(this, errorMsgIfAny, Toast.LENGTH_LONG).show()
}
stop!!.post {
stop!!.performClick()
}
}
}
}
this is try to change code...but err
class DroidSpeechActivity : Fragment(), View.OnClickListener,
OnDSListener,
OnDSPermissionsListener {
val TAG = "Activity_DroidSpeech"
private var droidSpeech: DroidSpeech? = null
private var finalSpeechResult: TextView? = null
// MARK: Activity Methods
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Setting the layout;[.
// Initializing the droid speech and setting the listener
droidSpeech =
DroidSpeech(activity?.applicationContext, activity?.getFragmentManager())
droidSpeech!!.setOnDroidSpeechListener(this)
droidSpeech!!.setShowRecognitionProgressView(false)
droidSpeech!!.setOneStepResultVerify(true)
droidSpeech!!.setRecognitionProgressMsgColor(Color.WHITE)
droidSpeech!!.setOneStepVerifyConfirmTextColor(Color.WHITE)
droidSpeech!!.setOneStepVerifyRetryTextColor(Color.WHITE)
droidSpeech!!.setPreferredLanguage("ko-KR")
start.setOnClickListener(this)
stop.setOnClickListener(this)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.activity_droid_speech, container, false)
}
override fun onPause() {
super.onPause()
if (stop!!.visibility == View.VISIBLE) {
stop!!.performClick()
}
}
override fun onDestroy() {
super.onDestroy()
if (stop!!.visibility == View.VISIBLE) {
stop!!.performClick()
}
}
// MARK: OnClickListener Method
override fun onClick(view: View) {
when (view.id) {
R.id.start -> {
// Starting droid speech
droidSpeech!!.startDroidSpeechRecognition()
// Setting the view visibilities when droid speech is running
start!!.visibility = View.GONE
stop!!.visibility = View.VISIBLE
}
R.id.stop -> {
// Closing droid speech
droidSpeech!!.closeDroidSpeechOperations()
stop!!.visibility = View.GONE
start!!.visibility = View.VISIBLE
}
}
}
// MARK: DroidSpeechListener Methods
override fun onDroidSpeechSupportedLanguages(currentSpeechLanguage: String, supportedSpeechLanguages: List<String>) {
Log.i(TAG, "Current speech language = $currentSpeechLanguage")
Log.i(TAG, "Supported speech languages = $supportedSpeechLanguages")
if (supportedSpeechLanguages.contains("ko-KR")) {
// Setting the droid speech preferred language as tamil if found
droidSpeech!!.setPreferredLanguage("ko-KR")
// Setting the confirm and retry text in tamil
droidSpeech!!.setOneStepVerifyConfirmText("check")
droidSpeech!!.setOneStepVerifyRetryText("no")
}
}
override fun onDroidSpeechRmsChanged(rmsChangedValue: Float) {
// Log.i(TAG, "Rms change value = " + rmsChangedValue);
}
override fun onDroidSpeechLiveResult(liveSpeechResult: String) {
Log.i(TAG, "Live speech result = $liveSpeechResult")
}
override fun onDroidSpeechFinalResult(finalSpeechResult: String) {
// Setting the final speech result
this.finalSpeechResult!!.text = finalSpeechResult
println("test$finalSpeechResult")
if (droidSpeech!!.continuousSpeechRecognition) {
val colorPallets1 = intArrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA)
val colorPallets2 = intArrayOf(Color.YELLOW, Color.RED, Color.CYAN, Color.BLUE, Color.GREEN)
// Setting random color pallets to the recognition progress view
droidSpeech!!.setRecognitionProgressViewColors(if (Random().nextInt(2) == 0) colorPallets1 else colorPallets2)
} else {
stop!!.visibility = View.GONE
start!!.visibility = View.VISIBLE
}
}
override fun onDroidSpeechClosedByUser() {
stop!!.visibility = View.GONE
start!!.visibility = View.VISIBLE
}
override fun onDroidSpeechError(errorMsg: String) {
// Speech error
stop!!.post { // Stop listening
stop!!.performClick()
}
}
// MARK: DroidSpeechPermissionsListener Method
override fun onDroidSpeechAudioPermissionStatus(audioPermissionGiven: Boolean, errorMsgIfAny: String) {
if (audioPermissionGiven) {
start!!.post { // Start listening
start!!.performClick()
}
} else {
if (errorMsgIfAny != null) {
// Permissions error
}
stop!!.post { // Stop listening
stop!!.performClick()
}
}
}
}
this is err message
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.example.Jachi3kki.DroidSpeechActivity.onCreateView(DroidSpeechActivity.kt:49)
DroidSpeechActivity.kt:49 line is this
start.setOnClickListener(this)
how to convert my AppCompatActivity to fragment?
Your code has some problems:
It's usually better to use the optional ? operator and don't force the cast with !! to avoid a crash by NullPointerException
You need to initialize your views in the onCreateView method by referring to the view instance.
Unlike activities, fragments don't call the setContentView method, so you can't refer directly to the layout items.
In my code I used the inline method setOnClickListener and I assumed that start and stop are Buttons,
if it's not the case you just need to change the type of the two views in their declaration.
Try to change your fragment's code like this:
class DroidSpeechActivity : Fragment(),
OnDSListener,
OnDSPermissionsListener {
val TAG = "Activity_DroidSpeech"
private var droidSpeech: DroidSpeech? = null
private var finalSpeechResult: TextView? = null
private var start: Button? = null
private var stop: Button? = null
// MARK: Activity Methods
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initializing the droid speech and setting the listener
droidSpeech = DroidSpeech(requireActivity().applicationContext, requireActivity().getFragmentManager()).apply {
setOnDroidSpeechListener(this)
setShowRecognitionProgressView(false)
setOneStepResultVerify(true)
setRecognitionProgressMsgColor(Color.WHITE)
setOneStepVerifyConfirmTextColor(Color.WHITE)
setOneStepVerifyRetryTextColor(Color.WHITE)
setPreferredLanguage("ko-KR")
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_droid_speech, container, false)
finalSpeechResult = view.findViewById<TextView>(R.id.finalSpeechResult)
start = view.findViewById<Button>(R.id.start).apply {
setOnClickListener { startClicked() }
}
stop = view.findViewById<Button>(R.id.start).apply {
setOnClickListener { stopClicked() }
}
return view
}
override fun onPause() {
super.onPause()
if (stop?.visibility == View.VISIBLE) {
stop?.performClick()
}
}
override fun onDestroy() {
super.onDestroy()
if (stop?.visibility == View.VISIBLE) {
stop?.performClick()
}
}
private fun startClicked() {
droidSpeech?.startDroidSpeechRecognition()
// Setting the view visibilities when droid speech is running
start?.visibility = View.GONE
stop?.visibility = View.VISIBLE
}
private fun stopClicked() {
droidSpeech?.closeDroidSpeechOperations()
stop?.visibility = View.GONE
start?.visibility = View.VISIBLE
}
// MARK: DroidSpeechListener Methods
override fun onDroidSpeechSupportedLanguages(currentSpeechLanguage: String, supportedSpeechLanguages: List<String>) {
Log.i(TAG, "Current speech language = $currentSpeechLanguage")
Log.i(TAG, "Supported speech languages = $supportedSpeechLanguages")
if (supportedSpeechLanguages.contains("ko-KR")) {
// Setting the droid speech preferred language as tamil if found
droidSpeech?.setPreferredLanguage("ko-KR")
// Setting the confirm and retry text in tamil
droidSpeech?.setOneStepVerifyConfirmText("check")
droidSpeech?.setOneStepVerifyRetryText("no")
}
}
override fun onDroidSpeechRmsChanged(rmsChangedValue: Float) {
// Log.i(TAG, "Rms change value = " + rmsChangedValue);
}
override fun onDroidSpeechLiveResult(liveSpeechResult: String) {
Log.i(TAG, "Live speech result = $liveSpeechResult")
}
override fun onDroidSpeechFinalResult(finalSpeechResult: String) {
// Setting the final speech result
finalSpeechResult?.text = finalSpeechResult
println("test$finalSpeechResult")
if (droidSpeech?.continuousSpeechRecognition == true) {
val colorPallets1 = intArrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA)
val colorPallets2 = intArrayOf(Color.YELLOW, Color.RED, Color.CYAN, Color.BLUE, Color.GREEN)
// Setting random color pallets to the recognition progress view
droidSpeech?.setRecognitionProgressViewColors(if (Random().nextInt(2) == 0) colorPallets1 else colorPallets2)
} else {
stop?.visibility = View.GONE
start?.visibility = View.VISIBLE
}
}
override fun onDroidSpeechClosedByUser() {
stop?.visibility = View.GONE
start?.visibility = View.VISIBLE
}
override fun onDroidSpeechError(errorMsg: String) {
// Speech error
stop?.post { // Stop listening
stop?.performClick()
}
}
// MARK: DroidSpeechPermissionsListener Method
override fun onDroidSpeechAudioPermissionStatus(audioPermissionGiven: Boolean, errorMsgIfAny: String) {
if (audioPermissionGiven) {
start?.post { // Start listening
start?.performClick()
}
} else {
if (errorMsgIfAny != null) {
// Permissions error
}
stop?.post { // Stop listening
stop?.performClick()
}
}
}
}
I have a small bluetooth project in kotlin. The code for it is below:
var mArrayAdapter: ArrayAdapter<String>? = null
val bluetoothAdapter: BluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
var myList: MutableList<String> = mutableListOf<String>()
var devices = ArrayList<BluetoothDevice>()
var devicesMap = HashMap<String, BluetoothDevice>()
class MainActivity : AppCompatActivity(), View.OnClickListener {
private val REQUEST_ENABLE_BT = 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.btn_scan).setOnClickListener(this)
checkBluetoothStatus()
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
registerReceiver(receiver, filter)
mArrayAdapter = ArrayAdapter(this, R.layout.dialog_select_device)
}
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action: String? = intent.action
when(action) {
BluetoothDevice.ACTION_FOUND -> {
val device: BluetoothDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val deviceName = device.name
myList.add(deviceName)
}
}
}
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_scan ->
startScan()
}
}
fun startScan() {
if (BluetoothAdapter.getDefaultAdapter().startDiscovery()) {
val myToast = Toast.makeText(this, myList.toString(), Toast.LENGTH_SHORT)
myToast.show()
}
}
}
When a user clicks a button, I want to be able to see a list of other bluetooth devices (which I thought the onReceive method would enable me to do). So I'm adding device names to the myList variable, which I'm then displaying in a toast. But at the moment when they click, nothing comes up. Grateful for your advice.
I need to ask permission for contacts and when application starts I'm asking,in ViewModel part I need to call method which requires permission. I need to check permission is granted by user or not and then call, but for checking permission I need to have access Activity. while in my ViewModel I don't have a reference to Activity and don't want to have, How I can overcome, the problem?
I just ran into this problem, and I decided to use make use of LiveData instead.
Core concept:
ViewModel has a LiveData on what permission request needs to be made
ViewModel has a method (essentially callback) that returns if permission is granted or not
SomeViewModel.kt:
class SomeViewModel : ViewModel() {
val permissionRequest = MutableLiveData<String>()
fun onPermissionResult(permission: String, granted: Boolean) {
TODO("whatever you need to do")
}
}
FragmentOrActivity.kt
class FragmentOrActivity : FragmentOrActivity() {
private viewModel: SomeViewModel by lazy {
ViewModelProviders.of(this).get(SomeViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
......
viewModel.permissionRequest.observe(this, Observer { permission ->
TODO("ask for permission, and then call viewModel.onPermissionResult aftwewards")
})
......
}
}
I have reworked the solution. The PermissionRequester object is everything you need to request permissions from any point where you have at least an application context. It uses its helper PermissionRequestActivity to accomplish this job.
#Parcelize
class PermissionResult(val permission: String, val state: State) : Parcelable
enum class State { GRANTED, DENIED_TEMPORARILY, DENIED_PERMANENTLY }
typealias Cancellable = () -> Unit
private const val PERMISSIONS_ARGUMENT_KEY = "PERMISSIONS_ARGUMENT_KEY"
private const val REQUEST_CODE_ARGUMENT_KEY = "REQUEST_CODE_ARGUMENT_KEY"
object PermissionRequester {
private val callbackMap = ConcurrentHashMap<Int, (List<PermissionResult>) -> Unit>(1)
private var requestCode = 256
get() {
requestCode = field--
return if (field < 0) 255 else field
}
fun requestPermissions(context: Context, vararg permissions: String, callback: (List<PermissionResult>) -> Unit): Cancellable {
val intent = Intent(context, PermissionRequestActivity::class.java)
.putExtra(PERMISSIONS_ARGUMENT_KEY, permissions)
.putExtra(REQUEST_CODE_ARGUMENT_KEY, requestCode)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
callbackMap[requestCode] = callback
return { callbackMap.remove(requestCode) }
}
internal fun onPermissionResult(responses: List<PermissionResult>, requestCode: Int) {
callbackMap[requestCode]?.invoke(responses)
callbackMap.remove(requestCode)
}
}
class PermissionRequestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
requestPermissions()
}
}
private fun requestPermissions() {
val permissions = intent?.getStringArrayExtra(PERMISSIONS_ARGUMENT_KEY) ?: arrayOf()
val requestCode = intent?.getIntExtra(REQUEST_CODE_ARGUMENT_KEY, -1) ?: -1
when {
permissions.isNotEmpty() && requestCode != -1 -> ActivityCompat.requestPermissions(this, permissions, requestCode)
else -> finishWithResult()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
val permissionResults = grantResults.zip(permissions).map { (grantResult, permission) ->
val state = when {
grantResult == PackageManager.PERMISSION_GRANTED -> State.GRANTED
ActivityCompat.shouldShowRequestPermissionRationale(this, permission) -> State.DENIED_TEMPORARILY
else -> State.DENIED_PERMANENTLY
}
PermissionResult(permission, state)
}
finishWithResult(permissionResults)
}
private fun finishWithResult(permissionResult: List<PermissionResult> = listOf()) {
val requestCode = intent?.getIntExtra(REQUEST_CODE_ARGUMENT_KEY, -1) ?: -1
PermissionRequester.onPermissionResult(permissionResult, requestCode)
finish()
}
}
Usage:
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val cancelRequest: Cancellable = requestPermission()
private fun requestPermission(): Cancellable {
return PermissionRequester.requestPermissions(getApplication(), "android.permission.SEND_SMS") {
if (it.firstOrNull()?.state == State.GRANTED) {
Toast.makeText(getApplication(), "GRANTED", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(getApplication(), "DENIED", Toast.LENGTH_LONG).show()
}
}
}
override fun onCleared() {
super.onCleared()
cancelRequest()
}
}
I did something like this:
create an abstract class that extends AndroidViewModel which gives you access to the application context:
abstract class BaseViewModel(application: Application) : AndroidViewModel(application), CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCleared() {
super.onCleared()
job.cancel()
}
}
Now, create your view model by extending the BaseViewModel class and you will have access to the application context
class AdminViewModel(application: Application) : BaseViewModel(application) {
.....
}
Now you always have access to a Context that you can use to get access to resources.