AndroidStudio Kotlin connect to bluetooth - android-studio

I've added <uses-permission android:name="android.permission.BLUETOOTH" /> to my manifest but the error
Missing permission required by BluetoothAdapter.isEnabled: android.permission.BLUETOOTH.
is still there.
Also, in ContextCompat.checkSelfPermission(...) what is the first parameter CONTEXT? The documentation https://developer.android.com/training/permissions/requesting does not say.
And am I correct that I need to disconnect and reconnect bluetooth whenever the app is not being used?
class MainActivity : AppCompatActivity() {
var bt: BluetoothAdapter? = null
var bts: BluetoothSocket? = null
val REQUEST_BLUETOOTH_PERMISSION: Int = 1
val REQUEST_BLUETOOTH_ENABLE: Int = 2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
{
Toast.makeText(
getApplicationContext(),
"Device does not support Bluetooth therefore this application cannot run.",
Toast.LENGTH_SHORT
).show();
return;
}
bt = BluetoothAdapter.getDefaultAdapter()
if (bt == null) {
// This device does not have Bluetooth.
Toast.makeText(
getApplicationContext(),
"Device does not have a Bluetooth adapter therefore this application cannot run.",
Toast.LENGTH_SHORT
).show();
return;
}
bluetoothConnect();
}
fun bluetoothConnect() {
if (ContextCompat.checkSelfPermission(
CONTEXT, // What is this? It's not explained at https://developer.android.com/training/permissions/requesting
Manifest.permission.BLUETOOTH
) == PackageManager.PERMISSION_GRANTED
) {
if (bt.isEnabled == false) { // Error: Missing permission required by BluetoothAdapter.isEnabled: android.permission.BLUETOOTH.
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_BLUETOOTH_ENABLE)
} else {
val pairedDevices: Set<BluetoothDevice>? = bt.bondedDevices
pairedDevices?.forEach { device ->
val deviceName = device.name
val deviceHardwareAddress = device.address // MAC address
}
}
}
else {
// Request permission. That will call back to onActivityResult which in the case of success will call this method again.
// Ask for permission.
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.BLUETOOTH),
REQUEST_BLUETOOTH_PERMISSION
)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_BLUETOOTH_PERMISSION) {
if (resultCode == RESULT_OK) {
bluetoothConnect();
} else {
Toast.makeText(
getApplicationContext(),
"This application cannot run because it does not have Bluetooth permission.",
Toast.LENGTH_SHORT
).show();
// Do we need to quit? How?
}
}
else if( requestCode == REQUEST_BLUETOOTH_ENABLE)
{
if(resultCode == RESULT_OK)
{
// try again
bluetoothConnect();
}
else {
Toast.makeText(
getApplicationContext(),
"This application cannot run because Bluetooth is not enabled and could not be enabled.",
Toast.LENGTH_SHORT
).show();
// Do we need to quit? How?
}
}
}
override fun onPause() {
super.onPause()
// Release Bluetooth
}
override fun onResume() {
super.onResume()
// Connect Bluetooth
}
override fun onStop() {
super.onStop()
// Release Bluetooth
}
override fun onStart() {
super.onStart()
// Connect Bluetooth
}
}
Edit:
added additional BT check to code,
adding manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rwb.btconnectortest">
<uses-permission android:name="android.permission.BLUETOOTH" />
<!--<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />-->
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/btconnectortestTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Just because comments are not good for this, let me give you a list of things you ought to do before you can act with Bluetooth. (apologies this is in Java because that's what I have right now, but very easy to translate to Kotlin if needed)
I'm doing this for BT LE (low energy) which is the preferred way for.. obvious reasons.
Did you add the permission(s) to the Manifest? You need something like
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<uses-permission android:name="android.permission.BLUETOOTH"/>
Make sure Bluetooth exists and is turned on...
// Does BLE exist?
if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
final BluetoothManager manager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
Now that you have a manager, you need to get the BluetoothAdapter:
BluetoothAdapter bluetoothAdapter = manager.getAdapter();
All this is fine in onCreate, but keep in mind that you have to check if BT is enabled every time the user resumes the activity (For it could have been turned off/disabled/revoked/etc).
Likely in onResume:
// obviously, you need to check that Bt adapter isn't null and all that,
// otherwise you ought to go back and "construct" it again, check permissions, etc.
adapter = getBTAdapter(); // do all the checks in there...
boolean bluetoothEnabled = adapter != null && adapter.isEnabled();
If the BT radio is off (user turning it off), you can programmatically enable it, if you have the corresponding permission (which I think is BT admin or similar, you're gonna have to search on that one, because it's been a while).
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> I believe it was.
Since BT is a radio that needs power, it will take a while (seconds) to turn on and be available. For this you need to "listen" with Yet Another broadcast receiver...
In other words, the activity will fire an intent (startActivityForResult(...)) telling Android to enable BT, you will subscribe to that broadcast to listen to the callback. Once android informs you that BT is on, you can go back to step 1 and start checking if it's enabled, you have permission, etc.
The callback is if I have not forgotten too much... looked like
public void onReceive(Context context, Intent intent) {
In there you ought to check for various BluetoothAdapter states... among them:
BluetoothAdapter.ACTION_STATE_CHANGED
This signals that the state changed, but another nested if is needed to determine to what state...
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE);
switch (state) {
case BluetoothAdapter.STATE_OFF:
case BluetoothAdapter.STATE_TURNING_OFF:
case BluetoothAdapter.STATE_TURNING_ON:
case BluetoothAdapter.STATE_ON:
}
Those are all the ones you care (check the BluetoothAdapter enum for more info).
In the ON you know BT is on... so..
Now you can tell the adapter that you want to scan...
adapter.startLeScan(callback);
(remember to call stopLeScan(callback) when you're done).
As each device is found, the callback will be called with the info you need to attempt to connect and pair (if needed).
The signature of the callback (LeScanCallback) is something like:
public void onScan(final BluetoothDevice device, int rssi, byte[] record);
(I'm typing by memory, so it may be a different name but you get the idea)
This is, as far as I can remember the old API.
API 21 has a ScanSettings.Builder() where you can specify how you want to scan, but it's essentially a similar method. Initiate scan, pass a callback and wait for results to show up.
You have various modes too:
SCAN_MODE_BALANCED: Balance battery efficiency and scan speed
SCAN_MODE_LOW_LATENCY: Prefer scan speed over battery
SCAN_MODE_LOW_POWER: Prefer battery efficiency over scan speed
SCAN_MODE_OPPORTUNISTIC: can't remember :) I think it was to use other scanner results 'around' you. Never used it.
Once you have identified the device you were looking for the BluetoothDevice has everything you need to tell BT to "connect" to it.
public void onScanResult(int callbackType, ScanResult scanResult) {
^ this is the signature of the "new" Scanner.
From that ScanResult, you can do:
int rssi = result.getRssi();
BluetoothDevice device = result.getDevice();
String advertiseName = device.getName();
String macAddress = device.getAddress();
If the scan fails for any reason, you get a callback on onScanFailed(int errorCode).
And again, there are various "reasons" (check the errorCode) why the scan failed.
Remember I may be mixing API 18 or API 21 "apis" here, but the concept is very similar in both.
Once you have finally grabbed a Device's MAC address... you can ask the adapter to try to connect to it:
BluetoothDevice device = adapter.getRemoteDevice(macAddress);
device.connectGatt(context, false, anotherCallback);
The callback is of BluetoothGattCallback and again, it has a bunch of methods among them onConnectionStateChange...
At this point you ought to read more about how Bluetooth works (and how it works on Android) because there are various modes (Gatt being one way) of operating with BT. It's impossible to know each and how/what you want to do once connected.
The rule of thumb will be: make sure you're prepared to having to re-pair or re-request permissions, because it's ultimately the user's choice to disable, turn off, walk-away, revoke permission, etc. at any point during this.
Good luck!

in manifest file add these two permission
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH"
#########################################################
*NOTE:i attached my bluetooth kotlin code , and its work with me. I enter code herehope this helpful
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var myBluetooth:BluetoothAdapter? = null
lateinit var mypairedDevices:Set<BluetoothDevice>
val Request_Enable_Blutooth=1
companion object {
val EXTRA_ADDRESS :String= "Device_Address"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
myBluetooth= BluetoothAdapter.getDefaultAdapter()
if (myBluetooth == null)
{
Toast.makeText(applicationContext, "Bluetooth Device Not Available", Toast.LENGTH_LONG).show()
}
if (!myBluetooth!!.isEnabled)
{
val enableBlutoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBlutoothIntent, Request_Enable_Blutooth)
}
binding.BTNPairedDevices.setOnClickListener {
pairedDeviceList()
}
}
private fun pairedDeviceList (){
mypairedDevices = myBluetooth!!.bondedDevices
val list : ArrayList<BluetoothDevice> = ArrayList()
if (!mypairedDevices.isEmpty())
{
for ( device:BluetoothDevice in mypairedDevices)
list.add(device)
//list.add(device.name() + "\n" + device.address())
Log.i("Device", "This is messeage")
}
else {
Toast.makeText(applicationContext, " NO PAIRED DEVICES FOUND", Toast.LENGTH_LONG).show()
}
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, list)
binding.DeviceListView.adapter = adapter
binding.DeviceListView.onItemClickListener = AdapterView.OnItemClickListener{ _, _, position, _ ->
val device: BluetoothDevice = list[position]
val address: String = device.address
val intent = Intent(this, LedController::class.java)
intent.putExtra(EXTRA_ADDRESS, address)
startActivity(intent)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Request_Enable_Blutooth)
{
if(resultCode ==Activity.RESULT_OK)
{
if (myBluetooth!!.isEnabled)
{ Toast.makeText(applicationContext, "Bluetooth Enabled", Toast.LENGTH_LONG).show()
}
else ( Toast.makeText(applicationContext, "Bluetooth Disabled", Toast.LENGTH_LONG).show()
)
}
} else if(resultCode == Activity.RESULT_CANCELED)
Toast.makeText(applicationContext, "Bluetooth has been canceled", Toast.LENGTH_LONG).show()
}
}

I restarted AndroidStudio and now the error has disappeared. What a complete piece of rubbish.
But now the layout is broken...

Related

Getting a list of Bluetooth devices Android Studio/Kotlin

Tell me how to write additional permissions or how to write it differently. I created such a small function to get a list of already paired devices. I did it according to the guide and everything works that way for a person there, but it highlights me in red and asks me to specify some permissions, and I can't find information on this. Below I will attach the function itself and the proposed solution.
P.S. I have registered Bluetooth and bluetooth_connect permissions in AndroidManifest.
private fun getPairedDevises(){
val pairedDevices: Set<BluetoothDevice>? = btAdapter?.bondedDevices
val tempList = ArrayList<ListItem>()
pairedDevices?.forEach {
tempList.add(ListItem(it.name, it.address))
Log.d("MyLog", "Name: ${it.name}")
}
adapter.submitList(tempList)
}
And this is shown if you click on the hint
private fun getPairedDevices(){
val pairedDevices: Set<BluetoothDevice>? = btAdapter?.bondedDevices
val tempList = ArrayList<ListItem>()
pairedDevices?.forEach {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.BLUETOOTH_CONNECT
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
tempList.add(ListItem(it.name, it.address))
Log.d("MyLog", "Name: ${it.name}")
}
adapter.submitList(tempList)
}
If I restart the project, it compiles, but the list of devices is not displayed

Why my savedInstanceState is not working?

I'm new to android studio with kotlin.
I want to make multiple choice quiz app, and I use data class and object constant to supply problem. If users choose correct choice, private var mCurrentPosition(Int) get plus 1 and setQuestion() work to change the problem, choices, and correctChoice.
To prevent the progress from being reset after the app is closed, I thought it would be okay if the int of mCurrentPosition was stored, so I use onSaveIntanceState. But progress is initialized after the app is closed...
class QuizActivity : AppCompatActivity() {
private var mCurrentPosition: Int = 1
private var mQuestion300List: ArrayList<Question300>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState != null) {
with(savedInstanceState) {
mCurrentPosition = getInt(STATE_SCORE)
}
} else {
mCurrentPosition = 1
}
setContentView(R.layout.activity_quiz)
val questionList = Constant.getQuestions()
Log.i("Question Size", "${questionList.size}")
mQuestion300List = Constant.getQuestions()
setQuestion()
tv_choice1.setOnClickListener {
if (tv_correctChoice.text.toString() == "1") {
mCurrentPosition ++
setQuestion()
} else {
tv_choice1.setBackgroundResource(R.drawable.shape_wrongchoice)
}
}
tv_choice2.setOnClickListener {
if (tv_correctChoice.text.toString() == "2") {
mCurrentPosition ++
setQuestion()
} else {
tv_choice2.setBackgroundResource(R.drawable.shape_wrongchoice)
}
}
tv_choice3.setOnClickListener {
if (tv_correctChoice.text.toString() == "3") {
mCurrentPosition ++
setQuestion()
} else {
tv_choice3.setBackgroundResource(R.drawable.shape_wrongchoice)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState?.run {
putInt(STATE_SCORE, mCurrentPosition)
}
super.onSaveInstanceState(outState)
}
companion object {
val STATE_SCORE = "score"
}
private fun setQuestion() {
val question300 = mQuestion300List!![mCurrentPosition-1]
tv_question.text = question300!!.question
tv_choice1.text = question300.choice1
tv_choice2.text = question300.choice2
tv_choice3.text = question300.choice3
tv_correctChoice.text = question300.correctChoice
tv_now.setText("${mCurrentPosition}")
tv_choice1.setBackgroundResource(R.drawable.shape_problem)
tv_choice2.setBackgroundResource(R.drawable.shape_problem)
tv_choice3.setBackgroundResource(R.drawable.shape_problem)
}
}
here is my app code. plz give me help :) thank you
savedInstanceState is really only meant for two things
surviving rotations, where the Activity gets destroyed and recreated
the system killing your app in the background - so when you return, the Activity needs to be recreated as it was, so the user doesn't see any difference between the app just being in the background, and the app being killed to save resources
When onCreate runs, if the Activity is being recreated from a previous state, you'll get a bundle passed in as savedInstanceState - this contains all the stuff you added in onSaveInstanceState before the app was stopped earlier. But if the user has closed the app (either by backing out with the back button, or swiping the app away in the task switcher etc.) then that's counted as a fresh start with no state to restore. And savedInstanceState will be null in onCreate (which is one way you can check if it's a fresh start or not).
So if you want to persist state even after the app is explicitly closed by the user, you'll need to use something else. Here's the docs on the subject - the typical way is to use SharedPreferences for small data, some kind of database like Room for larger state. DataStore is the new thing if you wanted to try that out

Xamarin Forms Android Push Notifications not received when app has been swiped closed

I have had push notifications configured in my Xamarin Forms app for the last year and this week decided to update to the new SDK, on iOS this was smooth sailing but on Android I am having problems.
I have tried to copy the documentation here: https://learn.microsoft.com/en-us/azure/notification-hubs/xamarin-notification-hubs-push-notifications-android-gcm and believe I am 90% of the way there as my app will happily receive push notifications when it is in the foreground and background, but my app cannot receive push notifications when it has been swiped closed (not force shut). I know this is 100% not a device issue as I tested the old configuration using the FirebaseService class and it works fine. I am also using 'Data' notifications.
I was able to find the following error message in the device log when a push notification is sent "Error (6621) / AndroidRuntime: java.lang.NullPointerException: Attempt to invoke interface method 'void com.microsoft.windowsazure.messaging.notificationhubs.NotificationListener.onPushNotificationReceived(android.content.Context, com.microsoft.windowsazure.messaging.notificationhubs.NotificationMessage)' on a null object reference"
Please see the code for my MainActivity class below.
[Activity(Label = "AndroidNotificationTest", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static readonly string CHANNEL_ID = "my_notification_channel";
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
// Listen for push notifications
NotificationHub.SetListener(new AzureListener());
// Start the SDK
NotificationHub.Start(this.Application, Constants.NotificationHubName, Constants.ListenConnectionString);
NotificationHub.AddTag("Developer");
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
CreateNotificationChannel();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
return;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "FCM Notifications", NotificationImportance.Default)
{
Description = "Firebase Cloud Messages appear in this channel"
};
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
internal class AzureListener : Java.Lang.Object, INotificationListener
{
public void OnPushNotificationReceived(Context context, INotificationMessage message)
{
Console.WriteLine($"Message received with title {message.Title} and body {message.Body}");
}
}
And my Android Manifest File.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.package.appname">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application android:label="AndroidNotificationTest.Android" android:theme="#style/MainTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
I have spent days on this but I am stumped at this stage on what the problem is.
Maybe my working code helps you.
I wrote this code before the new documentation with AzureListener class but it's still valid.
Xamarin forms: Push notification is not working on Android 7.1.2
//if i want more than one notification ,different unique value in every call
Random u = new Random ();
if (Build.VERSION.SdkInt >= BuildVersionCodes.O) {
string channelName = Resources.GetString (Resource.String.channel_name);
NotificationCompat.Builder notificationBuilder;
notificationBuilder = new NotificationCompat.Builder (this, channelName)
.SetContentTitle (title)
.SetSmallIcon (Resource.Drawable.ic_stat_g)
.SetContentText (messageBody)
.SetAutoCancel (true)
.SetContentIntent (pendingIntent);
var notificationManager = GetSystemService (Context.NotificationService) as NotificationManager;
NotificationChannel channel = new NotificationChannel (channelName, "notification channel", NotificationImportance.Default);
notificationManager.CreateNotificationChannel (channel);
notificationManager.Notify (u.Next(), notificationBuilder.Build ());
}
else
{
NotificationCompat.Builder notificationBuilder;
notificationBuilder = new NotificationCompat.Builder (this)
.SetContentTitle (title)
.SetSmallIcon (Resource.Drawable.ic_stat_g)
.SetContentText (messageBody)
.SetAutoCancel (true)
.SetContentIntent (pendingIntent);
var notificationManager = GetSystemService (Context.NotificationService) as NotificationManager;
notificationManager.Notify (u.Next(), notificationBuilder.Build ());
}

Kotlin: LoadURL onReceivedError() not firing

Simple kotlin app under android studio that makes a loadURL to a local address:-
The function often fails, probably due to local net latency with:
Web Page not available
The web page at http://192.168.1.144/apikey/webcam could not be loaded because:
net: ERR_ADDRESS_UNREACHABLE
I have
android:usesCleartextTraffic="true"
<uses-permission android:name="android.permission.INTERNET"/>
in the manifest, and the loadurl often is fine
In order to capture the error and provide a message an
onReceivedError()
action is used.
It never fires
Is the syntax of the onReceivedError correct? It refers to WebView rather than my instance myWebview (which causes a reference error), and I've moved the scope around to no effect.
The Android Studio comment says that the function is never used. A big hint, but I can't see which scope to place it in.
Or is this type of error one of those not caught by OnReceivedError. If so, how which function would?
Ideally I'd like to increase the 'wait' time of the LoadUrl function so that the lazy local IP can respond.
I've copied this from other examples.
I'd really welcome some help please
Here is my class code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val CHANNEL_ID = "only_channel"
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = descriptionText
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create channel to show notifications.
val channelId = getString(R.string.default_notification_channel_id)
val channelName = getString(R.string.default_notification_channel_name)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager?.createNotificationChannel(NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_HIGH))
}
val myWebView: WebView = findViewById(R.id.webview)
/*myWebView.loadUrl("https://amazon.co.uk")*/
myWebView.webViewClient = WebViewClient()
myWebView.setWebViewClient(object : WebViewClient() {
fun onReceivedError(view: WebView , errorCode: Int, description: String, failingUrl: String, getContext: Context) {
Log.i("WEB_VIEW_TEST", "error code:$errorCode")
Toast.makeText(getContext, "Webcam not reachable",Toast.LENGTH_SHORT ).show()
}
})
WebView.setWebContentsDebuggingEnabled(true)
/*5 March 2021*/
myWebView.clearCache(true)
myWebView.loadUrl("http://192.168.1.144/apikey/webcam")
val disable_button: Button = findViewById(R.id.disable)
disable_button.setOnClickListener {
myWebView.loadUrl("http://192.168.1.144/apikey/disable")
}
fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
Toast.makeText(this, "Webcam not reachable", Toast.LENGTH_SHORT).show()
}
}
}

Scan bluetooth devices not working on Android Things DP3

Scan Bluetooth Devices not working on Android Things DP3. Any idea/workaround? Not getting action BluetoothDevice.ACTION_FOUND.
I've added ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permisssion due changes in Android 6.0 but still not working.
https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html
Also android.permission.BLUETOOTH_ADMIN, android.permission.BLUETOOTH
I have checked, ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION over Android >=6.0 needs runtime permission but it give an error due Android Things limitation.
private void checkPermission() {
List<String> permissionsList = new ArrayList<String>();
List<String> permissionsNeeded = new ArrayList<String>();
// app
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION);
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
permissionsList.add(Manifest.permission.ACCESS_WIFI_STATE);
permissionsList.add(Manifest.permission.SET_TIME_ZONE);
// AndroidThingsLib
permissionsList.add(Manifest.permission.BLUETOOTH_ADMIN);
permissionsList.add(Manifest.permission.BLUETOOTH);
permissionsList.add(Manifest.permission.CHANGE_WIFI_STATE);
permissionsList.add(Manifest.permission.INTERNET);
permissionsList.add(Manifest.permission.ACCESS_NETWORK_STATE);
for (int i = 0; i < permissionsList.size(); i++){
if (checkSelfPermission(permissionsList.get(i)) == PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(permissionsList.get(i));
updateDataLogs(SourceEnum.System, "GRANTED: " + permissionsList.get(i));
} else {
permissionsNeeded.add(permissionsList.get(i));
updateDataLogs(SourceEnum.Error, "NOT GRANTED: " + permissionsList.get(i));
}
}
if (permissionsList.size()>0){
requestPermissions(permissionsNeeded.toArray(new String[permissionsNeeded.size()]),5678);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == 5678){
for (int i = 0; i < permissions.length; i++){
updateDataLogs(SourceEnum.Error, permissions[i] + " result " + grantResults[i]);
}
}
}
private void enableBluetoothScan() {
updateDataLogs(SourceEnum.System, "enableBluetoothScan");
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter.isEnabled()){
bluetoothAdapter.startDiscovery();
} else {
updateDataLogs(SourceEnum.Error, "Bluetooth Adapter not enabled");
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
updateDataLogs(SourceEnum.System, "DISCOVERY STARTED");
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
updateDataLogs(SourceEnum.System, "DISCOVERY FINISHED");
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
updateDataLogs(SourceEnum.System, "NEW DEVICE " + device.getName());
}
}
}
};
As noted in the Platform Overview, runtime permissions granting is not supported on Android Things because the device may not have an interactive display to show the dialog. Instead, Android Things falls back on the install-time permissions model for all permissions requested in the manifest. This means calls to methods like requestPermissions() aren't necessary and won't help you.
Regarding issues encountered during the current preview, the following excerpt is from the Release Notes:
Dangerous permissions requested by apps are not granted until the next device reboot. This includes new app installs and new elements in existing apps.
For the time being, when your app requests dangerous permissions (like location permissions), you need to reboot the device after install (one-time) to have those permissions granted during boot.
Thanks to your explanation what you did already, I was able to conquer this issue. Because you did not mark the question as solved yet:
build.gradle (app; thanks to 1 and 2):
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
<!-- update this to your version of the Android SDK Tools -->
buildToolsVersion "25.0.3"
defaultConfig {
minSdkVersion 24
}
}
dependencies {
<!-- update this to your version of Android Things -->
provided 'com.google.android.things:androidthings:0.4-devpreview'
}
AndroidManifest.xml (thanks to 1 and 2):
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package=" . . . ">
<!-- required for getState in startDiscovery -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!-- required for startDiscovery -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- required for the IntentFilter ACTION_FOUND -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- change to your label -->
<application android:label="Intel Edison">
<uses-library android:name="com.google.android.things"/>
<!-- change to the name of your Main Activity -->
<activity android:name=".HomeActivity">
<!-- Launch activity as default from Android Studio -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Launch activity automatically on boot -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.IOT_LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
HomeActivity.java:
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothDevice.ACTION_NAME_CHANGED);
registerReceiver(mReceiverBT, filter);
BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();
boolean started = mBtAdapter.startDiscovery();
if (false == started) {
<!-- in that case, catch BluetoothAdapter.STATE_ON in the receiver and start the Bluetooth Discovery then -->
mBtAdapter.enable();
}
With this code, my app started right away after booting the Intel Edison board and was able to discover nearby Bluetooth devices. Notes:
On this board, Android Things requires 69 seconds (Developer Preview 4) or 82 seconds (Developer Preview 3) to start my app.
In logcat, I saw "Not granting permission android.permission.ACCESS_COARSE_LOCATION to package … because it was previously installed without". I found no way around that; reported in Issue 62514750.
Actually, you might not need any ‘dangerous’ permission at all – for example, I do nothing on ACTION_FOUND – because ACTION_NANE_CHANGED is fired always here in my scenarios; reported in Issue 62524160.

Resources